/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.workspace.server.stack;
import com.jayway.restassured.response.Response;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.rest.ApiExceptionMapper;
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.stack.StackComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.stack.StackImpl;
import org.eclipse.che.api.workspace.server.model.impl.stack.StackSourceImpl;
import org.eclipse.che.api.workspace.server.spi.StackDao;
import org.eclipse.che.api.workspace.server.stack.image.StackIcon;
import org.eclipse.che.api.workspace.shared.dto.stack.StackComponentDto;
import org.eclipse.che.api.workspace.shared.dto.stack.StackDto;
import org.eclipse.che.api.workspace.shared.dto.stack.StackSourceDto;
import org.eclipse.che.api.workspace.shared.stack.Stack;
import org.eclipse.che.api.workspace.shared.stack.StackComponent;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.dto.server.DtoFactory;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.RequestFilter;
import org.everrest.core.impl.uri.UriBuilderImpl;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import javax.ws.rs.core.UriInfo;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.util.List;
import static com.jayway.restassured.RestAssured.given;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_GET_STACK_BY_ID;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_REMOVE_STACK;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD;
import static org.everrest.assured.JettyHttpServer.SECURE_PATH;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyList;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
/**
* Test for {@link @StackService}
*
* @author Alexander Andrienko
*/
@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
public class StackServiceTest {
private static final String STACK_ID = "java-default";
private static final String NAME = "Java";
private static final String DESCRIPTION = "Default Java Stack with JDK 8, Maven and Tomcat.";
private static final String USER_ID = "che";
private static final String CREATOR = USER_ID;
private static final String FOREIGN_CREATOR = "foreign_creator";
private static final String SCOPE = "general";
private static final String SOURCE_TYPE = "image";
private static final String SOURCE_ORIGIN = "codenvy/ubuntu_jdk8";
private static final String COMPONENT_NAME = "Java";
private static final String COMPONENT_VERSION = "1.8.0_45";
private static final String WORKSPACE_CONFIG_NAME = "default";
private static final String DEF_ENVIRONMENT_NAME = "default";
private static final String COMMAND_NAME = "newMaven";
private static final String COMMAND_TYPE = "mvn";
private static final String COMMAND_LINE = "mvn clean install -f ${current.project.path}";
private static final String ENVIRONMENT_NAME = "default";
private static final String ICON_MEDIA_TYPE = "image/svg+xml";
private static final String SVG_ICON = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
"<svg viewBox=\"0 0 200 200\" xmlns=\"http://www.w3.org/2000/svg\">\n" +
" <circle cx=\"100\" cy=\"100\" r=\"100\" fill=\"red\"/>\n" +
"</svg>";
@SuppressWarnings("unused")
static final EnvironmentFilter FILTER = new EnvironmentFilter();
@SuppressWarnings("unused")
static final ApiExceptionMapper MAPPER = new ApiExceptionMapper();
private List<String> tags = asList("java", "maven");
private StackDto stackDto;
private StackImpl stackImpl;
private StackImpl foreignStack;
private StackSourceImpl stackSourceImpl;
private List<StackComponent> componentsImpl;
private StackIcon stackIcon;
private StackSourceDto stackSourceDto;
private List<StackComponentDto> componentsDto;
@Mock
StackDao stackDao;
@Mock
UriInfo uriInfo;
@Mock
StackComponentImpl stackComponent;
@Mock
StackValidator validator;
@InjectMocks
StackService service;
@BeforeMethod
public void setUp() throws NoSuchFieldException, IllegalAccessException {
byte[] fileContent = STACK_ID.getBytes();
stackIcon = new StackIcon(ICON_MEDIA_TYPE, "image/svg+xml", fileContent);
componentsImpl = singletonList(new StackComponentImpl(COMPONENT_NAME, COMPONENT_VERSION));
stackSourceImpl = new StackSourceImpl(SOURCE_TYPE, SOURCE_ORIGIN);
CommandImpl command = new CommandImpl(COMMAND_NAME, COMMAND_LINE, COMMAND_TYPE);
EnvironmentImpl environment = new EnvironmentImpl(null,
null);
WorkspaceConfigImpl workspaceConfig = WorkspaceConfigImpl.builder()
.setName(WORKSPACE_CONFIG_NAME)
.setDefaultEnv(DEF_ENVIRONMENT_NAME)
.setCommands(singletonList(command))
.setEnvironments(singletonMap(ENVIRONMENT_NAME, environment))
.build();
stackSourceDto = newDto(StackSourceDto.class).withType(SOURCE_TYPE).withOrigin(SOURCE_ORIGIN);
StackComponentDto stackComponentDto = newDto(StackComponentDto.class).withName(COMPONENT_NAME).withVersion(COMPONENT_VERSION);
componentsDto = singletonList(stackComponentDto);
stackDto = DtoFactory.getInstance().createDto(StackDto.class).withId(STACK_ID)
.withName(NAME)
.withDescription(DESCRIPTION)
.withScope(SCOPE)
.withCreator(CREATOR)
.withTags(tags)
.withSource(stackSourceDto)
.withComponents(componentsDto);
stackImpl = StackImpl.builder().setId(STACK_ID)
.setName(NAME)
.setDescription(DESCRIPTION)
.setScope(SCOPE)
.setCreator(CREATOR)
.setTags(tags)
.setSource(stackSourceImpl)
.setComponents(componentsImpl)
.setWorkspaceConfig(workspaceConfig)
.setStackIcon(stackIcon)
.build();
foreignStack = StackImpl.builder().setId(STACK_ID)
.setName(NAME)
.setDescription(DESCRIPTION)
.setScope(SCOPE)
.setCreator(FOREIGN_CREATOR)
.setTags(tags)
.setSource(stackSourceImpl)
.setComponents(componentsImpl)
.setWorkspaceConfig(workspaceConfig)
.setStackIcon(stackIcon)
.build();
when(uriInfo.getBaseUriBuilder()).thenReturn(new UriBuilderImpl());
final Field uriField = service.getClass()
.getSuperclass()
.getDeclaredField("uriInfo");
uriField.setAccessible(true);
uriField.set(service, uriInfo);
}
/** Create stack */
@Test
public void newStackShouldBeCreatedForUser() throws ConflictException, ServerException {
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.body(stackDto)
.when()
.post(SECURE_PATH + "/stack");
assertEquals(response.getStatusCode(), 201);
verify(stackDao).create(any(StackImpl.class));
final StackDto stackDtoDescriptor = unwrapDto(response, StackDto.class);
assertEquals(stackDtoDescriptor.getName(), stackDto.getName());
assertEquals(stackDtoDescriptor.getCreator(), USER_ID);
assertEquals(stackDtoDescriptor.getDescription(), stackDto.getDescription());
assertEquals(stackDtoDescriptor.getTags(), stackDto.getTags());
assertEquals(stackDtoDescriptor.getComponents(), stackDto.getComponents());
assertEquals(stackDtoDescriptor.getSource(), stackDto.getSource());
assertEquals(stackDtoDescriptor.getScope(), stackDto.getScope());
assertEquals(stackDtoDescriptor.getLinks().size(), 2);
assertEquals(stackDtoDescriptor.getLinks().get(0).getRel(), LINK_REL_REMOVE_STACK);
assertEquals(stackDtoDescriptor.getLinks().get(1).getRel(), LINK_REL_GET_STACK_BY_ID);
}
// @Test
// public void shouldThrowBadRequestExceptionOnCreateStackWithEmptyBody() {
// final Response response = given().auth()
// .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
// .contentType(APPLICATION_JSON)
// .when()
// .post(SECURE_PATH + "/stack");
//
// assertEquals(response.getStatusCode(), 400);
// assertEquals(unwrapDto(response, ServiceError.class).getMessage(), "Stack required");
// }
// @Test
// public void shouldThrowBadRequestExceptionOnCreateStackWithEmptyName() {
// StackComponentDto stackComponentDto = newDto(StackComponentDto.class).withName("Java").withVersion("1.8.45");
// StackSourceDto stackSourceDto = newDto(StackSourceDto.class).withType("image").withOrigin("codenvy/ubuntu_jdk8");
// StackDto stackDto = newDto(StackDto.class).withId(USER_ID)
// .withDescription("")
// .withScope("Simple java stack for generation java projects")
// .withTags(asList("java", "maven"))
// .withCreator("che")
// .withComponents(singletonList(stackComponentDto))
// .withSource(stackSourceDto);
//
// Response response = given().auth()
// .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
// .contentType(APPLICATION_JSON)
// .body(stackDto)
// .when()
// .post(SECURE_PATH + "/stack");
//
// assertEquals(response.getStatusCode(), 400);
// assertEquals(unwrapDto(response, ServiceError.class).getMessage(), "Stack name required");
// }
/** Get stack by id */
@Test
public void stackByIdShouldBeReturned() throws NotFoundException, ServerException {
when(stackDao.getById(STACK_ID)).thenReturn(stackImpl);
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/stack/" + STACK_ID);
assertEquals(response.getStatusCode(), 200);
StackDto result = unwrapDto(response, StackDto.class);
assertEquals(result.getId(), stackImpl.getId());
assertEquals(result.getName(), stackImpl.getName());
assertEquals(result.getDescription(), stackImpl.getDescription());
assertEquals(result.getScope(), stackImpl.getScope());
assertEquals(result.getTags().get(0), stackImpl.getTags().get(0));
assertEquals(result.getTags().get(1), stackImpl.getTags().get(1));
assertEquals(result.getComponents().get(0).getName(), stackImpl.getComponents().get(0).getName());
assertEquals(result.getComponents().get(0).getVersion(), stackImpl.getComponents().get(0).getVersion());
assertEquals(result.getSource().getType(), stackImpl.getSource().getType());
assertEquals(result.getSource().getOrigin(), stackImpl.getSource().getOrigin());
assertEquals(result.getCreator(), stackImpl.getCreator());
}
@Test
public void stackShouldBeUpdated() throws NotFoundException, ServerException, ConflictException {
final String updatedDescription = "some description";
final String updatedScope = "advanced";
StackDto updatedStackDto = DtoFactory.getInstance().createDto(StackDto.class)
.withId(STACK_ID)
.withName(NAME)
.withDescription(updatedDescription)
.withScope(updatedScope)
.withCreator(CREATOR)
.withTags(tags)
.withSource(stackSourceDto)
.withComponents(componentsDto);
StackImpl updateStack = new StackImpl(stackImpl);
updateStack.setDescription(updatedDescription);
updateStack.setScope(updatedScope);
when(stackDao.getById(STACK_ID)).thenReturn(stackImpl).thenReturn(updateStack);
when(stackDao.update(any())).thenReturn(updateStack).thenReturn(updateStack);
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.content(updatedStackDto)
.when()
.put(SECURE_PATH + "/stack/" + STACK_ID);
assertEquals(response.getStatusCode(), 200);
StackDto result = unwrapDto(response, StackDto.class);
assertEquals(result.getId(), updatedStackDto.getId());
assertEquals(result.getName(), updatedStackDto.getName());
assertEquals(result.getDescription(), updatedStackDto.getDescription());
assertEquals(result.getScope(), updatedStackDto.getScope());
assertEquals(result.getTags().get(0), updatedStackDto.getTags().get(0));
assertEquals(result.getTags().get(1), updatedStackDto.getTags().get(1));
assertEquals(result.getComponents().get(0).getName(), updatedStackDto.getComponents().get(0).getName());
assertEquals(result.getComponents().get(0).getVersion(), updatedStackDto.getComponents().get(0).getVersion());
assertEquals(result.getSource().getType(), updatedStackDto.getSource().getType());
assertEquals(result.getSource().getOrigin(), updatedStackDto.getSource().getOrigin());
assertEquals(result.getCreator(), updatedStackDto.getCreator());
verify(stackDao).update(any());
verify(stackDao).getById(STACK_ID);
}
@Test
public void creatorShouldNotBeUpdated() throws ServerException, NotFoundException, ConflictException {
StackDto updatedStackDto = DtoFactory.getInstance().createDto(StackDto.class)
.withId(STACK_ID)
.withName(NAME)
.withDescription(DESCRIPTION)
.withScope(SCOPE)
.withCreator("creator changed")
.withTags(tags)
.withSource(stackSourceDto)
.withComponents(componentsDto);
when(stackDao.getById(anyString())).thenReturn(foreignStack);
when(stackDao.update(any())).thenReturn(foreignStack);
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.content(updatedStackDto)
.when()
.put(SECURE_PATH + "/stack/" + STACK_ID);
assertEquals(response.getStatusCode(), 200);
StackDto result = unwrapDto(response, StackDto.class);
assertEquals(result.getId(), updatedStackDto.getId());
assertEquals(result.getName(), updatedStackDto.getName());
assertEquals(result.getDescription(), updatedStackDto.getDescription());
assertEquals(result.getScope(), updatedStackDto.getScope());
assertEquals(result.getTags().get(0), updatedStackDto.getTags().get(0));
assertEquals(result.getTags().get(1), updatedStackDto.getTags().get(1));
assertEquals(result.getComponents().get(0).getName(), updatedStackDto.getComponents().get(0).getName());
assertEquals(result.getComponents().get(0).getVersion(), updatedStackDto.getComponents().get(0).getVersion());
assertEquals(result.getSource().getType(), updatedStackDto.getSource().getType());
assertEquals(result.getSource().getOrigin(), updatedStackDto.getSource().getOrigin());
assertEquals(result.getCreator(), FOREIGN_CREATOR);
verify(stackDao).update(any());
verify(stackDao).getById(STACK_ID);
}
/** Delete stack */
@Test
public void stackShouldBeDeleted() throws ServerException, NotFoundException {
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.delete(SECURE_PATH + "/stack/" + STACK_ID);
verify(stackDao).remove(eq(STACK_ID));
assertEquals(response.getStatusCode(), 204);
}
/** Search stack by tags */
@Test
public void shouldReturnsAllStacksWhenListTagsIsEmpty() throws ServerException {
StackImpl stack2 = new StackImpl(stackImpl);
stack2.setTags(singletonList("subversion"));
List<StackImpl> stacks = asList(stackImpl, stack2);
when(stackDao.searchStacks(anyString(), anyList(), anyInt(), anyInt())).thenReturn(stacks);
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/stack");
assertEquals(response.getStatusCode(), 200);
verify(stackDao).searchStacks(anyString(), anyList(), anyInt(), anyInt());
List<StackDto> result = unwrapListDto(response, StackDto.class);
assertEquals(result.size(), 2);
assertEquals(result.get(0).getName(), stackImpl.getName());
assertEquals(result.get(1).getName(), stack2.getName());
}
@Test
public void shouldReturnsStackByTagList() throws ServerException {
StackImpl stack2 = new StackImpl(stackImpl);
stack2.setTags(singletonList("Subversion"));
when(stackDao.searchStacks(anyString(), eq(singletonList("Subversion")), anyInt(), anyInt())).thenReturn(singletonList(stack2));
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/stack?tags=Subversion");
assertEquals(response.getStatusCode(), 200);
verify(stackDao).searchStacks(anyString(), eq(singletonList("Subversion")), anyInt(), anyInt());
List<StackDto> result = unwrapListDto(response, StackDto.class);
assertEquals(result.size(), 1);
assertEquals(result.get(0).getName(), stack2.getName());
}
/** Get icon by stack id */
@Test
public void shouldReturnIconByStackId() throws NotFoundException, ServerException {
when(stackDao.getById(stackImpl.getId())).thenReturn(stackImpl);
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/stack/" + stackImpl.getId() + "/icon");
assertEquals(response.getStatusCode(), 200);
verify(stackDao).getById(stackImpl.getId());
}
@Test
public void shouldThrowNotFoundExceptionWhenIconStackWasNotFound() throws NotFoundException, ServerException {
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/stack/" + stackImpl.getId() + "/icon");
assertEquals(response.getStatusCode(), 404);
String expectedErrorMessage = format("Stack with id '%s' was not found.", STACK_ID);
assertEquals(unwrapDto(response, ServiceError.class).getMessage(), expectedErrorMessage);
verify(stackDao).getById(stackImpl.getId());
}
@Test
public void shouldThrowNotFoundExceptionWhenIconWasNotFound() throws NotFoundException, ServerException {
StackImpl test = new StackImpl(stackImpl);
test.setStackIcon(null);
when(stackDao.getById(test.getId())).thenReturn(test);
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/stack/" + stackImpl.getId() + "/icon");
assertEquals(response.getStatusCode(), 404);
String expectedErrorMessage = format("Image for stack with id '%s' was not found.", STACK_ID);
assertEquals(unwrapDto(response, ServiceError.class).getMessage(), expectedErrorMessage);
verify(stackDao).getById(test.getId());
}
/** Delete icon by stack id */
@Test
public void stackIconShouldBeDeletedForUserOwner() throws NotFoundException, ConflictException, ServerException {
when(stackDao.getById(stackImpl.getId())).thenReturn(stackImpl);
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.delete(SECURE_PATH + "/stack/" + stackImpl.getId() + "/icon");
assertEquals(response.getStatusCode(), 204);
verify(stackDao).getById(stackImpl.getId());
verify(stackDao).update(stackImpl);
}
/** Update stack icon */
@Test
public void stackIconShouldBeUploadedForUserOwner() throws NotFoundException, ConflictException, ServerException, URISyntaxException {
when(stackDao.getById(stackImpl.getId())).thenReturn(stackImpl);
checkUploadIcon(stackImpl);
}
@Test
public void foreignStackIconShouldBeUploadedForUser() throws NotFoundException, ConflictException, ServerException {
when(stackDao.getById(foreignStack.getId())).thenReturn(foreignStack);
checkUploadIcon(foreignStack);
}
private void checkUploadIcon(Stack stack) throws NotFoundException, ServerException, ConflictException {
Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.multiPart("type-java.svg", SVG_ICON, "image/svg+xml")
.contentType(MULTIPART_FORM_DATA)
.post(SECURE_PATH + "/stack/" + stackImpl.getId() + "/icon");
assertEquals(response.getStatusCode(), 200);
verify(stackDao).getById(foreignStack.getId());
verify(stackDao).update(any());
}
private static <T> T unwrapDto(Response response, Class<T> dtoClass) {
return DtoFactory.getInstance().createDtoFromJson(response.body().print(), dtoClass);
}
private static <T> List<T> unwrapListDto(Response response, Class<T> dtoClass) {
return DtoFactory.getInstance().createListDtoFromJson(response.body().print(), dtoClass);
}
@Filter
public static class EnvironmentFilter implements RequestFilter {
public void doFilter(GenericContainerRequest request) {
EnvironmentContext.getCurrent().setSubject(new SubjectImpl("user", USER_ID, "token", false));
}
}
}