/******************************************************************************* * 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.spi.tck; import com.google.inject.Inject; 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.notification.EventService; import org.eclipse.che.api.machine.server.spi.SnapshotDao; import org.eclipse.che.api.workspace.server.event.BeforeStackRemovedEvent; import org.eclipse.che.api.workspace.server.event.StackPersistedEvent; 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.commons.test.tck.TckListener; import org.eclipse.che.commons.test.tck.repository.TckRepository; import org.eclipse.che.commons.test.tck.repository.TckRepositoryException; import org.eclipse.che.core.db.cascade.CascadeEventSubscriber; import org.eclipse.che.core.db.cascade.event.CascadeEvent; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.stream.Stream; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static org.eclipse.che.api.workspace.server.spi.tck.WorkspaceDaoTest.createWorkspaceConfig; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; /** * Tests {@link SnapshotDao} contract. * * @author Yevhenii Voevodin */ @Listeners(TckListener.class) @Test(suiteName = StackDaoTest.SUITE_NAME) public class StackDaoTest { public static final String SUITE_NAME = "StackDaoTck"; private static final int STACKS_SIZE = 5; private StackImpl[] stacks; @Inject private TckRepository<StackImpl> stackRepo; @Inject private StackDao stackDao; @Inject private EventService eventService; @BeforeMethod private void createStacks() throws TckRepositoryException { stacks = new StackImpl[STACKS_SIZE]; for (int i = 0; i < STACKS_SIZE; i++) { stacks[i] = createStack("stack-" + i, "name-" + i); } stackRepo.createAll(Stream.of(stacks).map(StackImpl::new).collect(toList())); } @AfterMethod private void removeStacks() throws TckRepositoryException { stackRepo.removeAll(); } @Test public void shouldGetById() throws Exception { final StackImpl stack = stacks[0]; assertEquals(stackDao.getById(stack.getId()), stack); } @Test(expectedExceptions = NotFoundException.class) public void shouldThrowNotFoundExceptionWhenGettingNonExistingStack() throws Exception { stackDao.getById("non-existing-stack"); } @Test(expectedExceptions = NullPointerException.class) public void shouldThrowNpeWhenGettingStackByNullKey() throws Exception { stackDao.getById(null); } @Test(dependsOnMethods = "shouldGetById") public void shouldCreateStack() throws Exception { final StackImpl stack = createStack("new-stack", "new-stack-name"); stackDao.create(stack); assertEquals(stackDao.getById(stack.getId()), new StackImpl(stack)); } @Test(dependsOnMethods = "shouldThrowNotFoundExceptionWhenGettingNonExistingStack", expectedExceptions = NotFoundException.class) public void shouldNotCreateStackWhenSubscriberThrowsExceptionOnStackStoring() throws Exception { final StackImpl stack = createStack("new-stack", "new-stack-name"); CascadeEventSubscriber<StackPersistedEvent> subscriber = mockCascadeEventSubscriber(); doThrow(new ConflictException("error")).when(subscriber).onCascadeEvent(any()); eventService.subscribe(subscriber, StackPersistedEvent.class); try { stackDao.create(stack); fail("StackDao#create had to throw conflict exception"); } catch (ConflictException ignored) { } eventService.unsubscribe(subscriber, StackPersistedEvent.class); stackDao.getById(stack.getId()); } @Test(expectedExceptions = ConflictException.class) public void shouldThrowConflictExceptionWhenCreatingStackWithIdThatAlreadyExists() throws Exception { final StackImpl stack = createStack(stacks[0].getId(), "new-name"); stackDao.create(stack); } @Test(expectedExceptions = ConflictException.class) public void shouldThrowConflictExceptionWhenCreatingStackWithNameThatAlreadyExists() throws Exception { final StackImpl stack = createStack("new-stack-id", stacks[0].getName()); stackDao.create(stack); } @Test(expectedExceptions = NullPointerException.class) public void shouldThrowNpeWhenCreatingNullStack() throws Exception { stackDao.create(null); } @Test(expectedExceptions = NotFoundException.class, dependsOnMethods = "shouldThrowNotFoundExceptionWhenGettingNonExistingStack") public void shouldRemoveStack() throws Exception { final StackImpl stack = stacks[0]; stackDao.remove(stack.getId()); // Should throw an exception stackDao.getById(stack.getId()); } @Test(dependsOnMethods = "shouldGetById") public void shouldNotRemoveStackWhenSubscriberThrowsExceptionOnStackRemoving() throws Exception { final StackImpl stack = stacks[0]; CascadeEventSubscriber<BeforeStackRemovedEvent> subscriber = mockCascadeEventSubscriber(); doThrow(new ServerException("error")).when(subscriber).onCascadeEvent(any()); eventService.subscribe(subscriber, BeforeStackRemovedEvent.class); try { stackDao.remove(stack.getId()); fail("StackDao#remove had to throw server exception"); } catch (ServerException ignored) { } assertEquals(stackDao.getById(stack.getId()), stack); eventService.unsubscribe(subscriber, BeforeStackRemovedEvent.class); } @Test public void shouldNotThrowAnyExceptionWhenRemovingNonExistingStack() throws Exception { stackDao.remove("non-existing"); } @Test(expectedExceptions = NullPointerException.class) public void shouldThrowNpeWhenRemovingNull() throws Exception { stackDao.remove(null); } @Test(dependsOnMethods = "shouldGetById") public void shouldUpdateStack() throws Exception { final StackImpl stack = stacks[0]; stack.setName("new-name"); stack.setCreator("new-creator"); stack.setDescription("new-description"); stack.setScope("new-scope"); stack.getTags().clear(); stack.getTags().add("new-tag"); // Remove an existing component stack.getComponents().remove(1); // Add a new component stack.getComponents().add(new StackComponentImpl("component3", "component3-version")); // Update an existing component final StackComponentImpl component = stack.getComponents().get(0); component.setName("new-name"); component.setVersion("new-version"); // Updating source final StackSourceImpl source = stack.getSource(); source.setType("new-type"); source.setOrigin("new-source"); // Set a new icon stack.setStackIcon(new StackIcon("new-name", "new-media", "new-data".getBytes())); stackDao.update(stack); assertEquals(stackDao.getById(stack.getId()), new StackImpl(stack)); } @Test(expectedExceptions = ConflictException.class) public void shouldNotUpdateStackIfNewNameIsReserved() throws Exception { final StackImpl stack = stacks[0]; stack.setName(stacks[1].getName()); stackDao.update(stack); } @Test(expectedExceptions = NotFoundException.class) public void shouldThrowNotFoundExceptionWhenUpdatingNonExistingStack() throws Exception { stackDao.update(createStack("new-stack", "new-stack-name")); } @Test(expectedExceptions = NullPointerException.class) public void shouldThrowNpeWhenUpdatingNullStack() throws Exception { stackDao.update(null); } @Test(dependsOnMethods = "shouldUpdateStack") public void shouldFindStacksWithSpecifiedTags() throws Exception { stacks[0].getTags().addAll(asList("search-tag1", "search-tag2")); stacks[1].getTags().addAll(asList("search-tag1", "non-search-tag")); stacks[2].getTags().addAll(asList("non-search-tag", "search-tag2")); stacks[3].getTags().addAll(asList("search-tag1", "search-tag2", "another-tag")); updateAll(); final List<StackImpl> found = stackDao.searchStacks(null, asList("search-tag1", "search-tag2"), 0, 0); found.forEach(s -> Collections.sort(s.getTags())); for (StackImpl stack : stacks) { Collections.sort(stack.getTags()); } assertEquals(new HashSet<>(found), new HashSet<>(asList(stacks[0], stacks[3]))); } @Test public void shouldReturnAllStacksWhenSearchingWithoutTags() throws Exception { final List<StackImpl> found = stackDao.searchStacks(null, null, 0, 0); found.forEach(s -> Collections.sort(s.getTags())); for (StackImpl stack : stacks) { Collections.sort(stack.getTags()); } assertEquals(new HashSet<>(found), new HashSet<>(asList(stacks))); } @Test public void shouldPublishStackPersistedEventAfterStackIsPersisted() throws Exception { final boolean[] isNotified = new boolean[] {false}; eventService.subscribe(event -> isNotified[0] = true, StackPersistedEvent.class); stackDao.create(createStack("test", "test")); assertTrue(isNotified[0], "Event subscriber notified"); } private void updateAll() throws ConflictException, NotFoundException, ServerException { for (StackImpl stack : stacks) { stackDao.update(stack); } } private static StackImpl createStack(String id, String name) { final StackImpl stack = StackImpl.builder() .setId(id) .setName(name) .setCreator("user123") .setDescription(id + "-description") .setScope(id + "-scope") .setTags(asList(id + "-tag1", id + "-tag2")) .setComponents(asList(new StackComponentImpl(id + "-component1", id + "-component1-version"), new StackComponentImpl(id + "-component2", id + "-component2-version"))) .setSource(new StackSourceImpl(id + "-type", id + "-origin")) .setStackIcon(new StackIcon(id + "-icon", id + "-media-type", "0x1234567890abcdef".getBytes())) .build(); final WorkspaceConfigImpl config = createWorkspaceConfig("test"); stack.setWorkspaceConfig(config); return stack; } private <T extends CascadeEvent> CascadeEventSubscriber<T> mockCascadeEventSubscriber() { @SuppressWarnings("unchecked") CascadeEventSubscriber<T> subscriber = mock(CascadeEventSubscriber.class); doCallRealMethod().when(subscriber).onEvent(any()); return subscriber; } }