/*
*
* * This file is part of the Hesperides distribution.
* * (https://github.com/voyages-sncf-technologies/hesperides)
* * Copyright (c) 2016 VSCT.
* *
* * Hesperides is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as
* * published by the Free Software Foundation, version 3.
* *
* * Hesperides 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
* * General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package com.vsct.dt.hesperides.storage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import com.google.common.eventbus.EventBus;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.vsct.dt.hesperides.exception.runtime.MissingResourceException;
import com.vsct.dt.hesperides.exception.runtime.StateLockedException;
import com.vsct.dt.hesperides.security.model.User;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import static junit.framework.TestCase.fail;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
import tests.type.UnitTests;
/**
* Created by wmontaz on 06/11/2014.
*/
@Category(UnitTests.class)
public class AbstractThreadAggregateTest {
static EventStore eventStore = mock(EventStore.class);
private static class AggregateTestImpl extends AbstractThreadAggregate {
/**
* Convenient class that wraps the thread executor of the aggregate.
*/
private ExecutorService singleThreadPool;
protected AggregateTestImpl() {
super(new EventBus(), eventStore);
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setDaemon(false)
.setNameFormat("TEST-%d")
.build();
this.singleThreadPool = Executors.newFixedThreadPool(1, threadFactory);
}
@Override
public String getStreamPrefix() {
return "TEST";
}
@Override
protected ExecutorService executorService() {
return this.singleThreadPool;
}
}
@Before
public void setUp() {
reset(eventStore);
}
@Test
public void should_apply_command_when_state_is_writable_using_threadlocal_user_informations() {
AggregateTestImpl testAggregate = new AggregateTestImpl();
HesperidesCommand command = mock(HesperidesCommand.class);
Object event = new Object();
when(command.apply()).thenReturn(event);
when(eventStore.store("TEST-stream", event, UserInfo.UNTRACKED, command)).thenReturn(event);
testAggregate.tryAtomic("stream", command);
verify(command).apply();
verify(eventStore).store("TEST-stream", event, UserInfo.UNTRACKED, command);
}
@Test
public void should_use_not_tracked_user_when_no_user_provided() {
AggregateTestImpl testAggregate = new AggregateTestImpl();
HesperidesCommand command = mock(HesperidesCommand.class);
Object event = new Object();
when(command.apply()).thenReturn(event);
UserInfo userInfo = new UserInfo(User.UNTRACKED.getUsername());
when(eventStore.store("TEST-stream", event, userInfo, command)).thenReturn(event);
testAggregate.tryAtomic("stream", command);
verify(command).apply();
verify(eventStore).store("TEST-stream", event, UserInfo.UNTRACKED, command);
}
@Test
public void should_block_state_when_unknown_runtime_exception_occurs() {
AggregateTestImpl testAggregate = new AggregateTestImpl();
HesperidesCommand command = mock(HesperidesCommand.class);
when(command.apply()).thenThrow(new RuntimeException());
try {
testAggregate.tryAtomic("stream", command);
} catch(Exception e) {
assertThat(testAggregate.isWritable()).isFalse();
return;
}
fail();
}
//This is a way to detect an excpetionthrown by aggregate that is qualified
//HesperidesExceptions guaranties that the state is still useable
@Test
public void should_not_block_state_when_hesperides_runtime_exception_occurs() {
AggregateTestImpl testAggregate = new AggregateTestImpl();
HesperidesCommand command = mock(HesperidesCommand.class);
when(command.apply()).thenThrow(new MissingResourceException("Something is missing"));
try {
testAggregate.tryAtomic("stream", command);
} catch(Exception e) {
assertThat(testAggregate.isWritable()).isTrue();
return;
}
fail();
}
@Test
public void should_lock_when_eventstore_throws_runtime() {
AggregateTestImpl testAggregate = new AggregateTestImpl();
HesperidesCommand command = mock(HesperidesCommand.class);
Object event = new Object();
when(command.apply()).thenReturn(event);
when(eventStore.store("TEST-stream", event, UserInfo.UNTRACKED, command)).thenThrow(new RuntimeException());
try {
testAggregate.tryAtomic("stream", command);
} catch(Exception e) {
assertThat(testAggregate.isWritable()).isFalse();
return;
}
fail();
}
@Test(expected = StateLockedException.class)
public void should_fail_when_state_is_not_writable() {
AggregateTestImpl testAggregate = new AggregateTestImpl();
HesperidesCommand command = mock(HesperidesCommand.class);
when(command.apply()).thenThrow(new RuntimeException());
/* Block the state */
try {
testAggregate.tryAtomic("stream", command);
} catch(Exception e) {
testAggregate.tryAtomic("stream", command);
}
fail();
}
}