Java tutorial
/* * Copyright (C) 2011-2014 eXo Platform SAS. * * This file is part of eXo Acceptance Webapp. * * eXo Acceptance Webapp is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * eXo Acceptance Webapp software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with eXo Acceptance Webapp; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see <http://www.gnu.org/licenses/>. */ package org.exoplatform.acceptance.rest.administration.vcs; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; import org.exoplatform.acceptance.model.vcs.VCSRepository; import org.exoplatform.acceptance.rest.JsonErrorHandler; import org.exoplatform.acceptance.service.EntityNotFoundException; import org.exoplatform.acceptance.service.vcs.VCSRepositoryService; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.inject.Inject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.dao.DuplicateKeyException; import org.springframework.http.MediaType; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver; import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod; /** * */ @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(locations = { "classpath:/org/exoplatform/acceptance/rest/spring-test.xml" }) @ActiveProfiles("test") public class VCSRepositoryCRUDControllerTest { private MockMvc mockMvc; @InjectMocks private VCSRepositoryCRUDController controllerUnderTest; @Mock private VCSRepositoryService vcsRepositoryServiceMock; @Inject private JsonErrorHandler jsonErrorHandler; public static byte[] convertObjectToJsonBytes(Object object) throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); return mapper.writeValueAsBytes(object); } @Before public void setUp() { // this must be called for the @Mock annotations above to be processed // and for the mock service to be injected into the controller under // test. MockitoAnnotations.initMocks(this); // We initialize the MVC Mock for our controller mockMvc = standaloneSetup(controllerUnderTest).setHandlerExceptionResolvers(createExceptionResolver()) .build(); } private ExceptionHandlerExceptionResolver createExceptionResolver() { ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() { protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) { Method method = new ExceptionHandlerMethodResolver(JsonErrorHandler.class).resolveMethod(exception); return new ServletInvocableHandlerMethod(jsonErrorHandler, method); } }; List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); messageConverters.add(new FormHttpMessageConverter()); messageConverters.add(new StringHttpMessageConverter()); messageConverters.add(new MappingJackson2HttpMessageConverter()); exceptionResolver.setMessageConverters(messageConverters); exceptionResolver.afterPropertiesSet(); return exceptionResolver; } @Test public void findAll_VCSRepositoriesFound_ShouldReturnFoundVCSRepositoryEntriesWithDataFiltered() throws Exception { VCSRepository first_repository = new VCSRepository("first_repository", "1"); VCSRepository second_repository = new VCSRepository("second_repository", "2"); when(vcsRepositoryServiceMock.findAll()).thenReturn(Arrays.asList(first_repository, second_repository)); mockMvc.perform(get("/admin/vcs/repository")).andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(jsonPath("$", hasSize(2))) .andExpect(jsonPath("$[0].id", is(first_repository.getId()))) .andExpect(jsonPath("$[0].name", is(first_repository.getName()))) .andExpect(jsonPath("$[0].type", is(first_repository.getType().toString()))) .andExpect(jsonPath("$[1].id", is(second_repository.getId()))) .andExpect(jsonPath("$[1].name", is(second_repository.getName()))) .andExpect(jsonPath("$[1].type", is(second_repository.getType().toString()))); verify(vcsRepositoryServiceMock, times(1)).findAll(); verifyNoMoreInteractions(vcsRepositoryServiceMock); } @Test @SuppressWarnings("unchecked") public void findById_VCSRepositoryEntryNotFound_ShouldReturnHttpStatusCode404() throws Exception { when(vcsRepositoryServiceMock.findOne("1")).thenThrow(EntityNotFoundException.class); mockMvc.perform(get("/admin/vcs/repository/{id}", "1")).andExpect(status().isNotFound()); verify(vcsRepositoryServiceMock, times(1)).findOne("1"); verifyNoMoreInteractions(vcsRepositoryServiceMock); } @Test public void findById_VCSRepositoryEntryFound_ShouldReturnFoundVCSRepositoryEntry() throws Exception { VCSRepository first_repository = new VCSRepository("first_repository", "1"); when(vcsRepositoryServiceMock.findOne(first_repository.getId())).thenReturn(first_repository); mockMvc.perform(get("/admin/vcs/repository/{id}", first_repository.getId())).andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.type", is(first_repository.getType().toString()))) .andExpect(jsonPath("$.name", is(first_repository.getName()))) .andExpect(jsonPath("$.id", is(first_repository.getId()))); verify(vcsRepositoryServiceMock, times(1)).findOne(first_repository.getId()); verifyNoMoreInteractions(vcsRepositoryServiceMock); } @Test public void add_NameIsTooLong_ShouldReturnValidationErrorForName() throws Exception { VCSRepository invalidObject = new VCSRepository(Strings.padEnd("a", 65, 'a')); invalidObject.addRemoteRepository("acceptance", "git@github.com:exoplatform/acceptance.git"); mockMvc.perform(post("/admin/vcs/repository").contentType(MediaType.APPLICATION_JSON) .content(convertObjectToJsonBytes(invalidObject))).andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.fieldErrors", hasSize(1))) .andExpect(jsonPath("$.fieldErrors[*].field", containsInAnyOrder("name"))).andExpect( jsonPath("$.fieldErrors[*].message", containsInAnyOrder("size must be between 3 and 64"))); verifyZeroInteractions(vcsRepositoryServiceMock); } @Test public void add_NoRemote_ShouldReturnValidationErrorForRemotes() throws Exception { VCSRepository invalidObject = new VCSRepository(Strings.padEnd("a", 10, 'a')); mockMvc.perform(post("/admin/vcs/repository").contentType(MediaType.APPLICATION_JSON) .content(convertObjectToJsonBytes(invalidObject))).andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.fieldErrors", hasSize(1))) .andExpect(jsonPath("$.fieldErrors[*].field", containsInAnyOrder("remoteRepositories"))) .andExpect(jsonPath("$.fieldErrors[*].message", containsInAnyOrder("size must be between 1 and " + Integer.MAX_VALUE))); verifyZeroInteractions(vcsRepositoryServiceMock); } @Test public void add_NameAlreadyExists_ShouldReturnValidationError() throws Exception { VCSRepository objectToCreate = new VCSRepository(Strings.padEnd("a", 10, 'a')); objectToCreate.addRemoteRepository("acceptance", "git@github.com:exoplatform/acceptance.git"); String exMsg = "{ \"serverUsed\" : \"localhost/127.0.0.1:27017\" , \"err\" : \"E11000 duplicate key error index: acceptance" + ".credentials.$name dup key: { : \\\"" + objectToCreate.getName() + "\\\" }\" , \"code\" : 11000 , \"n\" : 0 , \"connectionId\" : 5 , \"ok\" : 1.0}"; String errorMsg = "E11000 duplicate key error index: acceptance.credentials.$name dup key: { : \"" + objectToCreate.getName() + "\" }"; when(vcsRepositoryServiceMock.updateOrCreate(any(VCSRepository.class))) .thenThrow(new DuplicateKeyException("", new Exception(exMsg))); mockMvc.perform(post("/admin/vcs/repository").contentType(MediaType.APPLICATION_JSON) .content(convertObjectToJsonBytes(objectToCreate))).andExpect(status().isConflict()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.globalErrors", hasSize(1))) .andExpect(jsonPath("$.globalErrors[*].objectName", containsInAnyOrder("Duplicated Key Error"))) .andExpect(jsonPath("$.globalErrors[*].message", containsInAnyOrder(errorMsg))); ArgumentCaptor<VCSRepository> dtoCaptor = ArgumentCaptor.forClass(VCSRepository.class); verify(vcsRepositoryServiceMock, times(1)).updateOrCreate(dtoCaptor.capture()); verifyNoMoreInteractions(vcsRepositoryServiceMock); VCSRepository dtoArgument = dtoCaptor.getValue(); assertNull(dtoArgument.getId()); assertThat(dtoArgument.getName(), is(objectToCreate.getName())); assertThat(dtoArgument.getType(), is(objectToCreate.getType())); } @Test public void add_NewVCSRepositoryEntry_ShouldAddVCSRepositoryEntryAndReturnAddedEntry() throws Exception { VCSRepository objectToCreate = new VCSRepository(Strings.padEnd("a", 10, 'a')); objectToCreate.addRemoteRepository("acceptance", "git@github.com:exoplatform/acceptance.git"); VCSRepository objectCreated = new VCSRepository(Strings.padEnd("a", 10, 'a'), "1"); objectCreated.addRemoteRepository("acceptance", "git@github.com:exoplatform/acceptance.git"); when(vcsRepositoryServiceMock.updateOrCreate(any(VCSRepository.class))).thenReturn(objectCreated); mockMvc.perform(post("/admin/vcs/repository").contentType(MediaType.APPLICATION_JSON) .content(convertObjectToJsonBytes(objectToCreate))).andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.id", is(objectCreated.getId()))) .andExpect(jsonPath("$.name", is(objectCreated.getName()))) .andExpect(jsonPath("$.type", is(objectCreated.getType().toString()))); ArgumentCaptor<VCSRepository> dtoCaptor = ArgumentCaptor.forClass(VCSRepository.class); verify(vcsRepositoryServiceMock, times(1)).updateOrCreate(dtoCaptor.capture()); verifyNoMoreInteractions(vcsRepositoryServiceMock); VCSRepository dtoArgument = dtoCaptor.getValue(); assertNull(dtoArgument.getId()); assertThat(dtoArgument.getName(), is(objectToCreate.getName())); assertThat(dtoArgument.getType(), is(objectToCreate.getType())); } @Test public void delete_VCSRepositoryIsNotFound_ShouldReturnHttpStatusCode404() throws Exception { doThrow(new EntityNotFoundException("foo")).when(vcsRepositoryServiceMock).delete("foo"); mockMvc.perform(delete("/admin/vcs/repository/{id}", "foo")).andExpect(status().isNotFound()); verify(vcsRepositoryServiceMock, times(1)).delete("foo"); verifyNoMoreInteractions(vcsRepositoryServiceMock); } @Test public void update_EmptyVCSRepositoryEntry_ShouldReturnValidationErrorForName() throws Exception { VCSRepository invalidObject = new VCSRepository("", "1"); invalidObject.addRemoteRepository("acceptance", "git@github.com:exoplatform/acceptance.git"); mockMvc.perform(put("/admin/vcs/repository/{id}", invalidObject.getId()) .contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(invalidObject))) .andExpect(status().isBadRequest()).andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.fieldErrors", hasSize(1))) .andExpect(jsonPath("$.fieldErrors[*].field", containsInAnyOrder("name"))).andExpect( jsonPath("$.fieldErrors[*].message", containsInAnyOrder("size must be between 3 and 64"))); verifyZeroInteractions(vcsRepositoryServiceMock); } @Test public void update_NameIsTooLong_ShouldReturnValidationErrorsForName() throws Exception { VCSRepository invalidObject = new VCSRepository(Strings.padEnd("a", 65, 'a'), "foo"); invalidObject.addRemoteRepository("acceptance", "git@github.com:exoplatform/acceptance.git"); mockMvc.perform(put("/admin/vcs/repository/{id}", invalidObject.getId()) .contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(invalidObject))) .andExpect(status().isBadRequest()).andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.fieldErrors", hasSize(1))) .andExpect(jsonPath("$.fieldErrors[*].field", containsInAnyOrder("name"))).andExpect( jsonPath("$.fieldErrors[*].message", containsInAnyOrder("size must be between 3 and 64"))); verifyZeroInteractions(vcsRepositoryServiceMock); } @Test public void update_NoRemote_ShouldReturnValidationErrorsForRemotes() throws Exception { VCSRepository invalidObject = new VCSRepository(Strings.padEnd("a", 10, 'a'), "foo"); mockMvc.perform(put("/admin/vcs/repository/{id}", invalidObject.getId()) .contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(invalidObject))) .andExpect(status().isBadRequest()).andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.fieldErrors", hasSize(1))) .andExpect(jsonPath("$.fieldErrors[*].field", containsInAnyOrder("remoteRepositories"))) .andExpect(jsonPath("$.fieldErrors[*].message", containsInAnyOrder("size must be between 1 and " + Integer.MAX_VALUE))); verifyZeroInteractions(vcsRepositoryServiceMock); } @Test public void update_VCSRepositoryEntryNotFound_ShouldReturnHttpStatusCode404() throws Exception { VCSRepository absentObject = new VCSRepository(Strings.padEnd("a", 10, 'a'), "unavailable"); absentObject.addRemoteRepository("acceptance", "git@github.com:exoplatform/acceptance.git"); when(vcsRepositoryServiceMock.update(any(VCSRepository.class))).thenThrow(new EntityNotFoundException("")); mockMvc.perform(put("/admin/vcs/repository/{id}", absentObject.getId()) .contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(absentObject))) .andExpect(status().isNotFound()); ArgumentCaptor<VCSRepository> dtoCaptor = ArgumentCaptor.forClass(VCSRepository.class); verify(vcsRepositoryServiceMock, times(1)).update(dtoCaptor.capture()); verifyNoMoreInteractions(vcsRepositoryServiceMock); VCSRepository dtoArgument = dtoCaptor.getValue(); assertThat(dtoArgument.getId(), is(absentObject.getId())); assertThat(dtoArgument.getType(), is(absentObject.getType())); assertThat(dtoArgument.getName(), is(absentObject.getName())); } @Test public void update_VCSRepositoryEntryFound_ShouldUpdateVCSRepositoryEntryAndReturnIt() throws Exception { VCSRepository objectSent = new VCSRepository(Strings.padEnd("a", 10, 'a'), "foo"); objectSent.addRemoteRepository("acceptance", "git@github.com:exoplatform/acceptance.git"); VCSRepository objectReceived = new VCSRepository(Strings.padEnd("c", 10, 'c'), "foo"); objectReceived.addRemoteRepository("acceptance", "git@github.com:exoplatform/acceptance.git"); when(vcsRepositoryServiceMock.update(any(VCSRepository.class))).thenReturn(objectReceived); mockMvc.perform(put("/admin/vcs/repository/{id}", objectSent.getId()) .contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(objectSent))) .andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.id", is(objectReceived.getId()))) .andExpect(jsonPath("$.name", is(objectReceived.getName()))) .andExpect(jsonPath("$.type", is(objectReceived.getType().toString()))); ArgumentCaptor<VCSRepository> dtoCaptor = ArgumentCaptor.forClass(VCSRepository.class); verify(vcsRepositoryServiceMock, times(1)).update(dtoCaptor.capture()); verifyNoMoreInteractions(vcsRepositoryServiceMock); VCSRepository dtoArgument = dtoCaptor.getValue(); assertThat(dtoArgument.getId(), is(objectSent.getId())); assertThat(dtoArgument.getType(), is(objectSent.getType())); assertThat(dtoArgument.getName(), is(objectSent.getName())); } @Test public void delete_VCSRepositoryEntryFound_ShouldDeleteVCSRepositoryEntryAndReturnHttpStatusCode204() throws Exception { doNothing().when(vcsRepositoryServiceMock).delete("1"); mockMvc.perform(delete("/admin/vcs/repository/{id}", "1")).andExpect(status().isNoContent()); verify(vcsRepositoryServiceMock, times(1)).delete("1"); verifyNoMoreInteractions(vcsRepositoryServiceMock); } }