/**
* ***************************************************************************
* Copyright (c) 2010 Qcadoo Limited
* Project: Qcadoo MES
* Version: 1.4
*
* This file is part of Qcadoo.
*
* Qcadoo is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* ***************************************************************************
*/
package com.qcadoo.mes.states.aop;
import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.*;
import java.util.Collections;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.common.collect.Lists;
import com.qcadoo.mes.states.MockStateChangeDescriber;
import com.qcadoo.mes.states.StateChangeContext;
import com.qcadoo.mes.states.StateChangeEntityDescriber;
import com.qcadoo.mes.states.StateChangeTest;
import com.qcadoo.mes.states.annotation.RunInPhase;
import com.qcadoo.mes.states.constants.StateChangeStatus;
import com.qcadoo.mes.states.exception.StateChangeException;
import com.qcadoo.mes.states.messages.constants.StateMessageType;
import com.qcadoo.model.api.DataDefinition;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.EntityList;
public class AbstractStateChangeAspectTest extends StateChangeTest {
private static final String STATE_FIELD_NAME = "state";
private static final String STATE_FIELD_VALUE = "someState";
@Test
public final void checkPrecedencePointcutDefinitions() {
assertEquals("com.qcadoo.mes.states.aop.StateChangePhaseAspect", StateChangePhaseAspect.class.getCanonicalName());
assertEquals("com.qcadoo.mes.states.aop.RunInPhaseAspect", RunInPhaseAspect.class.getCanonicalName());
}
@Aspect
public static class TestStateChangeAspect extends AbstractStateListenerAspect {
@RunInPhase(1)
@org.aspectj.lang.annotation.Before("changeStateExecution(stateChangeContext)")
public void markEntityBefore(final StateChangeContext stateChangeContext) {
final Entity stateChange = stateChangeContext.getStateChangeEntity();
stateChange.setField("marked", true);
}
@Pointcut("this(TestStateChangeService)")
protected void targetServicePointcut() {
}
}
@Aspect
public static class TestStateChangeService extends AbstractStateChangeAspect {
@Override
public void changeState(final StateChangeContext stateChangeContext) {
final StateChangeEntityDescriber describer = stateChangeContext.getDescriber();
final Entity stateChangeEntity = stateChangeContext.getStateChangeEntity();
Entity targetEntity = stateChangeEntity.getBelongsToField(describer.getOwnerFieldName());
targetEntity.setField(describer.getOwnerStateFieldName(),
stateChangeEntity.getField(describer.getTargetStateFieldName()));
}
@Override
public StateChangeEntityDescriber getChangeEntityDescriber() {
return new MockStateChangeDescriber();
}
@Override
protected void changeStatePhase(final StateChangeContext stateChangeContext, final int phaseNumber) {
}
}
@Aspect
public static class AnotherStateChangeService extends AbstractStateChangeAspect {
@Override
public void changeState(final StateChangeContext stateChangeContext) {
final StateChangeEntityDescriber describer = stateChangeContext.getDescriber();
final Entity stateChangeEntity = stateChangeContext.getStateChangeEntity();
Entity targetEntity = stateChangeEntity.getBelongsToField(describer.getOwnerFieldName());
targetEntity.setField(describer.getOwnerStateFieldName(),
stateChangeEntity.getField(describer.getTargetStateFieldName()));
}
@Override
protected void changeStatePhase(final StateChangeContext stateChangeContext, final int phaseNumber) {
}
@Override
public StateChangeEntityDescriber getChangeEntityDescriber() {
return new MockStateChangeDescriber();
}
}
@Aspect
public static class AlmostRealStateChangeService extends AbstractStateChangeAspect {
@Override
public StateChangeEntityDescriber getChangeEntityDescriber() {
return new MockStateChangeDescriber();
}
@Override
protected void changeStatePhase(final StateChangeContext stateChangeContext, final int phaseNumber) {
}
}
@Aspect
public static class ExceptionThrowingStateChangeService extends AbstractStateChangeAspect {
@Override
public StateChangeEntityDescriber getChangeEntityDescriber() {
return new MockStateChangeDescriber();
}
@Override
protected void changeStatePhase(final StateChangeContext stateChangeContext, final int phaseNumber) {
throw new RuntimeException();
}
}
@Mock
private Entity ownerEntity;
@Mock
private DataDefinition dataDefinition;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
stubStateChangeEntity(DESCRIBER);
EntityList emptyEntityList = mockEmptyEntityList();
stubStateChangeContext();
stubOwner();
given(stateChangeEntity.getHasManyField("messages")).willReturn(emptyEntityList);
given(DESCRIBER.getOwnerDataDefinition().save(Mockito.any(Entity.class))).willAnswer(new Answer<Entity>() {
@Override
public Entity answer(final InvocationOnMock invocation) throws Throwable {
return (Entity) invocation.getArguments()[0];
}
});
}
@Test
public final void shouldFireListenersAndChangeState() {
// given
TestStateChangeService testStateChangeService = new TestStateChangeService();
given(stateChangeEntity.getBelongsToField(DESCRIBER.getOwnerFieldName())).willReturn(ownerEntity);
stubStringField(stateChangeEntity, DESCRIBER.getTargetStateFieldName(), STATE_FIELD_VALUE);
stubEntityMock("", "");
// when
testStateChangeService.changeState(stateChangeContext);
// then
verify(stateChangeEntity).setField("marked", true);
verify(ownerEntity).setField(STATE_FIELD_NAME, STATE_FIELD_VALUE);
}
@Test
public final void shouldNotFireListenersForOtherStateChangeService() {
// given
AnotherStateChangeService anotherStateChangeService = new AnotherStateChangeService();
given(stateChangeEntity.getBelongsToField(DESCRIBER.getOwnerFieldName())).willReturn(ownerEntity);
stubStringField(stateChangeEntity, DESCRIBER.getTargetStateFieldName(), STATE_FIELD_VALUE);
stubEntityMock("", "");
// when
anotherStateChangeService.changeState(stateChangeContext);
// then
verify(stateChangeEntity, never()).setField("marked", true);
verify(ownerEntity).setField(STATE_FIELD_NAME, STATE_FIELD_VALUE);
}
@Test
public final void shouldNotFireChangeStateIfStateChangeWasSuccessfulFinished() {
// given
AnotherStateChangeService anotherStateChangeService = new AnotherStateChangeService();
given(stateChangeEntity.getBelongsToField(DESCRIBER.getOwnerFieldName())).willReturn(ownerEntity);
given(stateChangeEntity.getStringField(DESCRIBER.getStatusFieldName())).willReturn(
StateChangeStatus.SUCCESSFUL.getStringValue());
mockStateChangeStatus(stateChangeEntity, StateChangeStatus.SUCCESSFUL);
// when
anotherStateChangeService.changeState(stateChangeContext);
// then
verify(ownerEntity, never()).setField(Mockito.eq(STATE_FIELD_NAME), Mockito.any());
}
@Test
public final void shouldNotFireChangeStateIfStateChangeWasFailureFinished() {
// given
AnotherStateChangeService anotherStateChangeService = new AnotherStateChangeService();
given(stateChangeEntity.getBelongsToField(DESCRIBER.getOwnerFieldName())).willReturn(ownerEntity);
given(stateChangeEntity.getStringField(DESCRIBER.getStatusFieldName())).willReturn(
StateChangeStatus.SUCCESSFUL.getStringValue());
mockStateChangeStatus(stateChangeEntity, StateChangeStatus.FAILURE);
// when
anotherStateChangeService.changeState(stateChangeContext);
// then
verify(ownerEntity, never()).setField(Mockito.eq(STATE_FIELD_NAME), Mockito.any());
}
@Test
public final void shouldNotFireChangeStateIfStateChangeWasPaused() {
// given
AnotherStateChangeService anotherStateChangeService = new AnotherStateChangeService();
given(stateChangeEntity.getBelongsToField(DESCRIBER.getOwnerFieldName())).willReturn(ownerEntity);
given(stateChangeEntity.getStringField(DESCRIBER.getStatusFieldName())).willReturn(
StateChangeStatus.SUCCESSFUL.getStringValue());
mockStateChangeStatus(stateChangeEntity, StateChangeStatus.PAUSED);
// when
anotherStateChangeService.changeState(stateChangeContext);
// then
verify(ownerEntity, never()).setField(Mockito.eq(STATE_FIELD_NAME), Mockito.any());
}
@Test
public final void shouldNotFireListenersIfStateChangeWasAlreadyFinished() {
// given
AnotherStateChangeService anotherStateChangeService = new AnotherStateChangeService();
given(stateChangeEntity.getBelongsToField(DESCRIBER.getOwnerFieldName())).willReturn(ownerEntity);
given(stateChangeEntity.getBooleanField(DESCRIBER.getStatusFieldName())).willReturn(true);
given(stateChangeEntity.getField(DESCRIBER.getStatusFieldName())).willReturn(true);
// when
anotherStateChangeService.changeState(stateChangeContext);
// then
verify(stateChangeEntity, never()).setField("marked", true);
}
@Test
public final void shouldNotFireChangeStateIfStateChangeHaveErrorMessages() {
// given
AnotherStateChangeService anotherStateChangeService = new AnotherStateChangeService();
EntityList messagesEntityList = mockEntityList(Lists.newArrayList(mockMessage(StateMessageType.FAILURE, "fail")));
given(stateChangeEntity.getHasManyField(DESCRIBER.getMessagesFieldName())).willReturn(messagesEntityList);
given(stateChangeEntity.getField(DESCRIBER.getMessagesFieldName())).willReturn(messagesEntityList);
// when
anotherStateChangeService.changeState(stateChangeContext);
// then
verify(ownerEntity, never()).setField(Mockito.eq(STATE_FIELD_NAME), Mockito.any());
}
@Test
public final void shouldNotFireListenersIfStateChangeHaveErrorMessages() {
// given
AnotherStateChangeService anotherStateChangeService = new AnotherStateChangeService();
EntityList messagesEntityList = mockEntityList(Lists.newArrayList(mockMessage(StateMessageType.FAILURE, "fail")));
given(stateChangeEntity.getHasManyField(DESCRIBER.getMessagesFieldName())).willReturn(messagesEntityList);
given(stateChangeEntity.getField(DESCRIBER.getMessagesFieldName())).willReturn(messagesEntityList);
// when
anotherStateChangeService.changeState(stateChangeContext);
// then
verify(stateChangeEntity, never()).setField("marked", true);
}
@Test
public final void shouldNotPerformEntityStateChangeIfOwnerHasValidationErrors() {
// given
AlmostRealStateChangeService stateChangeService = new AlmostRealStateChangeService();
given(stateChangeContext.isOwnerValid()).willReturn(true, false);
// when
stateChangeService.changeState(stateChangeContext);
// then
verify(stateChangeContext).setStatus(StateChangeStatus.FAILURE);
}
@Test
public final void shouldMarkStateChangeAsFailureAndRethrowExceptionWhenStateChangePhaseThrowsException() {
// given
ExceptionThrowingStateChangeService stateChangeService = new ExceptionThrowingStateChangeService();
try {
// when
stateChangeService.changeState(stateChangeContext);
} catch (StateChangeException e) {
// then
verify(stateChangeContext).setStatus(StateChangeStatus.FAILURE);
verify(stateChangeContext, Mockito.atLeastOnce()).save();
}
}
@Test
public final void shouldMarkStateChangeAsFailureAndRethrowExceptionWhenOwnerEntityValidatorThrowException() {
// given
AlmostRealStateChangeService stateChangeService = new AlmostRealStateChangeService();
given(ownerDD.callValidators(Mockito.any(Entity.class))).willThrow(new RuntimeException());
try {
// when
stateChangeService.changeState(stateChangeContext);
} catch (StateChangeException e) {
// then
verify(stateChangeContext).setStatus(StateChangeStatus.FAILURE);
verify(stateChangeContext, Mockito.atLeastOnce()).save();
}
}
@Test
public final void shouldMarkStateChangeAsFailureWhenOwnerIsInvalid() {
// given
AlmostRealStateChangeService stateChangeService = new AlmostRealStateChangeService();
given(owner.isValid()).willReturn(false);
given(ownerDD.callValidators(owner)).willReturn(false);
// when
stateChangeService.changeState(stateChangeContext);
// then
verify(stateChangeContext).setStatus(StateChangeStatus.FAILURE);
verify(stateChangeContext, Mockito.atLeastOnce()).save();
}
@SuppressWarnings("unchecked")
private EntityList mockEmptyEntityList() {
EntityList entityList = mock(EntityList.class);
given(entityList.isEmpty()).willReturn(true);
given(entityList.iterator()).willReturn(Collections.EMPTY_LIST.iterator());
return entityList;
}
private void stubEntityMock(final String pluginIdentifier, final String modelName) {
given(dataDefinition.getPluginIdentifier()).willReturn(pluginIdentifier);
given(dataDefinition.getName()).willReturn(modelName);
}
}