/*******************************************************************************
* 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.machine.server.recipe;
import com.google.common.collect.FluentIterable;
import com.jayway.restassured.response.Response;
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.spi.RecipeDao;
import org.eclipse.che.api.machine.shared.dto.recipe.NewRecipe;
import org.eclipse.che.api.machine.shared.dto.recipe.RecipeDescriptor;
import org.eclipse.che.api.machine.shared.dto.recipe.RecipeUpdate;
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.util.List;
import static com.jayway.restassured.RestAssured.given;
import static java.util.Arrays.asList;
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.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
/**
* @author Eugene Voevodin
*/
@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
public class RecipeServiceTest {
@SuppressWarnings("unused")
static final EnvironmentFilter FILTER = new EnvironmentFilter();
@SuppressWarnings("unused")
static final ApiExceptionMapper MAPPER = new ApiExceptionMapper();
static final String USER_ID = "user123";
@Mock
RecipeDao recipeDao;
@Mock
UriInfo uriInfo;
@InjectMocks
RecipeService service;
@BeforeMethod
public void setUpUriInfo() throws NoSuchFieldException, IllegalAccessException {
when(uriInfo.getBaseUriBuilder()).thenReturn(new UriBuilderImpl());
final Field uriField = service.getClass()
.getSuperclass()
.getDeclaredField("uriInfo");
uriField.setAccessible(true);
uriField.set(service, uriInfo);
}
@Test
public void shouldThrowBadRequestExceptionOnCreateRecipeWithNullBody() {
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.post(SECURE_PATH + "/recipe");
assertEquals(response.getStatusCode(), 400);
assertEquals(unwrapDto(response, ServiceError.class).getMessage(), "Recipe required");
}
@Test
public void shouldThrowBadRequestExceptionOnCreateRecipeWithNewRecipeWhichDoesNotHaveType() {
final NewRecipe newRecipe = newDto(NewRecipe.class).withScript("FROM ubuntu\n");
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(newRecipe)
.when()
.post(SECURE_PATH + "/recipe");
assertEquals(response.getStatusCode(), 400);
assertEquals(unwrapDto(response, ServiceError.class).getMessage(), "Recipe type required");
}
@Test
public void shouldThrowBadRequestExceptionOnCreateRecipeWithNewRecipeWhichDoesNotHaveScript() {
final NewRecipe newRecipe = newDto(NewRecipe.class).withType("docker");
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(newRecipe)
.when()
.post(SECURE_PATH + "/recipe");
assertEquals(response.getStatusCode(), 400);
assertEquals(unwrapDto(response, ServiceError.class).getMessage(), "Recipe script required");
}
@Test
public void shouldThrowBadRequestExceptionWhenCreatingRecipeWithoutName() {
final NewRecipe newRecipe = newDto(NewRecipe.class).withType("docker").withScript("script");
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(newRecipe)
.when()
.post(SECURE_PATH + "/recipe");
assertEquals(response.getStatusCode(), 400);
assertEquals(unwrapDto(response, ServiceError.class).getMessage(), "Recipe name required");
}
@Test
public void shouldCreateNewRecipe() throws Exception {
final NewRecipe newRecipe = newDto(NewRecipe.class).withType("docker")
.withName("name")
.withScript("FROM ubuntu\n")
.withTags(asList("java", "mongo"));
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(newRecipe)
.when()
.post(SECURE_PATH + "/recipe");
assertEquals(response.getStatusCode(), 201);
verify(recipeDao).create(any(RecipeImpl.class));
final RecipeDescriptor descriptor = unwrapDto(response, RecipeDescriptor.class);
assertNotNull(descriptor.getId());
assertEquals(descriptor.getName(), newRecipe.getName());
assertEquals(descriptor.getCreator(), USER_ID);
assertEquals(descriptor.getScript(), newRecipe.getScript());
assertEquals(descriptor.getTags(), newRecipe.getTags());
}
@Test
public void shouldBeAbleToGetRecipeScript() throws Exception {
final RecipeImpl recipe = new RecipeImpl().withCreator("other-user")
.withId("recipe123")
.withScript("FROM ubuntu\n");
when(recipeDao.getById(recipe.getId())).thenReturn(recipe);
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/recipe/" + recipe.getId() + "/script");
assertEquals(response.getStatusCode(), 200);
assertEquals(response.getBody().print(), recipe.getScript());
}
@Test
public void shouldBeAbleToGetRecipe() throws Exception {
final RecipeImpl recipe = new RecipeImpl().withCreator("someone2")
.withId("recipe123")
.withName("name")
.withType("docker")
.withScript("FROM ubuntu\n")
.withTags(asList("java", "mognodb"));
when(recipeDao.getById(recipe.getId())).thenReturn(recipe);
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/recipe/" + recipe.getId());
assertEquals(response.getStatusCode(), 200);
final RecipeDescriptor descriptor = unwrapDto(response, RecipeDescriptor.class);
assertEquals(descriptor.getId(), recipe.getId());
assertEquals(descriptor.getName(), recipe.getName());
assertEquals(descriptor.getType(), recipe.getType());
assertEquals(descriptor.getScript(), recipe.getScript());
assertEquals(descriptor.getTags(), recipe.getTags());
assertEquals(descriptor.getCreator(), recipe.getCreator());
}
@Test
public void shouldBeAbleToSearchRecipes() throws Exception {
final RecipeImpl recipe1 = new RecipeImpl().withId("id1")
.withCreator(USER_ID)
.withType("docker")
.withScript("script1 content")
.withTags(asList("java"));
final RecipeImpl recipe2 = new RecipeImpl().withId("id2")
.withCreator(USER_ID)
.withType("docker")
.withScript("script2 content")
.withTags(asList("java", "mongodb"));
when(recipeDao.search(eq("user123"),
eq(asList("java", "mongodb")),
eq("docker"),
any(int.class),
any(int.class))).thenReturn(asList(recipe1, recipe2));
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.queryParameter("tags", asList("java", "mongodb"))
.queryParameter("type", "docker")
.get(SECURE_PATH + "/recipe");
assertEquals(response.getStatusCode(), 200);
assertEquals(unwrapDtoList(response, RecipeDescriptor.class).size(), 2);
}
@Test
public void shouldBeAbleToRemoveRecipe() throws Exception {
final RecipeImpl recipe = new RecipeImpl().withId("id")
.withCreator(USER_ID)
.withType("docker")
.withScript("script1 content")
.withTags(asList("java"));
when(recipeDao.getById(recipe.getId())).thenReturn(recipe);
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.delete(SECURE_PATH + "/recipe/" + recipe.getId());
assertEquals(response.getStatusCode(), 204);
verify(recipeDao).remove(recipe.getId());
}
@Test
public void shouldBeAbleToUpdateRecipe() throws Exception {
final RecipeImpl recipe = new RecipeImpl().withId("id")
.withCreator(USER_ID)
.withType("docker")
.withScript("script1 content")
.withTags(asList("java"));
final RecipeImpl updatedRecipe = new RecipeImpl(recipe).withType("new-type")
.withScript("new script content")
.withTags(asList("java", "mongodb"));
when(recipeDao.update(any())).thenReturn(updatedRecipe);
RecipeUpdate update = newDto(RecipeUpdate.class).withId(recipe.getId())
.withType("new-type")
.withScript("new script content")
.withTags(asList("java", "mongodb"));
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(update)
.when()
.put(SECURE_PATH + "/recipe");
assertEquals(response.getStatusCode(), 200);
verify(recipeDao).update(any(RecipeImpl.class));
}
@Test
public void shouldThrowBadRequestExceptionWhenRecipeUpdateIsNull() throws Exception {
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.put(SECURE_PATH + "/recipe");
assertEquals(response.getStatusCode(), 400);
}
@Test
public void shouldThrowBadRequestExceptionWhenUpdatingRecipeWithNullId() throws Exception {
final Response response = given().auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(newDto(RecipeUpdate.class))
.when()
.put(SECURE_PATH + "/recipe");
assertEquals(response.getStatusCode(), 400);
}
@Filter
public static class EnvironmentFilter implements RequestFilter {
public void doFilter(GenericContainerRequest request) {
EnvironmentContext.getCurrent().setSubject(new SubjectImpl("user", USER_ID, "token", false));
}
}
private static <T> T unwrapDto(Response response, Class<T> dtoClass) {
return DtoFactory.getInstance().createDtoFromJson(response.body().print(), dtoClass);
}
private static <T> List<T> unwrapDtoList(Response response, Class<T> dtoClass) {
return FluentIterable.from(DtoFactory.getInstance().createListDtoFromJson(response.body().print(), dtoClass)).toList();
}
}