diff --git a/tapas-tasks/pom.xml b/tapas-tasks/pom.xml
index 7dcf6ae..6dcc158 100644
--- a/tapas-tasks/pom.xml
+++ b/tapas-tasks/pom.xml
@@ -37,6 +37,16 @@
spring-boot-starter-data-mongodb
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ de.codecentric
+ chaos-monkey-spring-boot
+ 2.5.4
+
+
org.projectlombok
lombok
@@ -47,6 +57,11 @@
spring-boot-starter-test
test
+
+ org.mockito
+ mockito-core
+ 2.21.0
+
org.springframework.boot
@@ -74,6 +89,10 @@
org.eclipse.paho.client.mqttv3
1.2.0
+
+ org.springframework.boot
+ spring-boot-test
+
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java
index d78de83..9776064 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java
@@ -11,11 +11,14 @@ import ch.unisg.tapastasks.tasks.domain.NewTaskAddedEvent;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+
import javax.transaction.Transactional;
@RequiredArgsConstructor
@Component
@Transactional
+@Service("AddNewTaskToTaskList")
public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase {
private final NewTaskAddedEventPort newTaskAddedEventPort;
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/RetrieveTaskFromTaskListService.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/RetrieveTaskFromTaskListService.java
index 5e5ec29..39026f5 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/RetrieveTaskFromTaskListService.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/RetrieveTaskFromTaskListService.java
@@ -7,6 +7,7 @@ import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.Optional;
@@ -14,6 +15,7 @@ import java.util.Optional;
@RequiredArgsConstructor
@Component
@Transactional
+@Service("RetrieveTaskFromTaskList")
public class RetrieveTaskFromTaskListService implements RetrieveTaskFromTaskListUseCase {
private final LoadTaskPort loadTaskFromRepositoryPort;
diff --git a/tapas-tasks/src/main/resources/application.properties b/tapas-tasks/src/main/resources/application.properties
index badc0f8..7588f89 100644
--- a/tapas-tasks/src/main/resources/application.properties
+++ b/tapas-tasks/src/main/resources/application.properties
@@ -1,7 +1,38 @@
server.port=8081
-//spring.data.mongodb.uri=mongodb://127.0.0.1:27017
-spring.data.mongodb.uri=mongodb://root:8nP7s0a@mongodb:27017/
+spring.data.mongodb.uri=mongodb://127.0.0.1:27017
+#spring.data.mongodb.uri=mongodb://root:8nP7s0a@mongodb:27017/
spring.data.mongodb.database=tapas-tasks
baseuri=https://tapas-tasks.86-119-34-23.nip.io/
+spring.profiles.active=chaos-monkey
+chaos.monkey.enabled=true
+management.endpoint.chaosmonkey.enabled=true
+management.endpoint.chaosmonkeyjmx.enabled=true
+# include specific endpoints
+management.endpoints.web.exposure.include=health,info,chaosmonkey
+chaos.monkey.watcher.controller=true
+chaos.monkey.watcher.restController=true
+chaos.monkey.watcher.service=true
+chaos.monkey.watcher.repository=true
+chaos.monkey.watcher.component=true
+#Chaos Monkey configs taken from here: https://www.baeldung.com/spring-boot-chaos-monkey
+
+#Latency Assault
+#chaos.monkey.assaults.latencyActive=true
+#chaos.monkey.assaults.latencyRangeStart=3000
+#chaos.monkey.assaults.latencyRangeEnd=15000
+
+#Exception Assault
+#chaos.monkey.assaults.latencyActive=false
+#chaos.monkey.assaults.exceptionsActive=true
+#chaos.monkey.assaults.killApplicationActive=false
+
+#AppKiller Assault
+#chaos.monkey.assaults.latencyActive=false
+#chaos.monkey.assaults.exceptionsActive=false
+#chaos.monkey.assaults.killApplicationActive=true
+
+#Chaos Monkey assaults via REST to endpoint /actuator/chaosmonkey/assaults/
+#https://softwarehut.com/blog/tech/chaos-monkey
+#https://codecentric.github.io/chaos-monkey-spring-boot/latest/
diff --git a/tapas-tasks/src/test/java/ch/unisg/tapastasks/AddNewTaskToTaskListSystemTest.java b/tapas-tasks/src/test/java/ch/unisg/tapastasks/AddNewTaskToTaskListSystemTest.java
new file mode 100644
index 0000000..e17c2e2
--- /dev/null
+++ b/tapas-tasks/src/test/java/ch/unisg/tapastasks/AddNewTaskToTaskListSystemTest.java
@@ -0,0 +1,76 @@
+package ch.unisg.tapastasks;
+
+import ch.unisg.tapastasks.tasks.adapter.in.formats.TaskJsonRepresentation;
+import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
+import ch.unisg.tapastasks.tasks.domain.Task;
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.*;
+
+import static org.assertj.core.api.BDDAssertions.*;
+
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class AddNewTaskToTaskListSystemTest {
+
+ @Autowired
+ private TestRestTemplate restTemplate;
+
+ @Autowired
+ private AddTaskPort addTaskPort;
+
+ @Test
+ void addNewTaskToTaskList() throws JSONException {
+
+ Task.TaskName taskName = new Task.TaskName("system-integration-test-task");
+ Task.TaskType taskType = new Task.TaskType("system-integration-test-type");
+ Task.OriginalTaskUri originalTaskUri = new Task.OriginalTaskUri("example.org");
+
+ ResponseEntity response = whenAddNewTaskToEmptyList(taskName, taskType, originalTaskUri);
+
+ JSONObject responseJson = new JSONObject(response.getBody().toString());
+ String respTaskId = responseJson.getString("taskId");
+ String respTaskName = responseJson.getString("taskName");
+ String respTaskType = responseJson.getString("taskType");
+
+ then(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
+ then(respTaskId).isNotEmpty();
+ then(respTaskName).isEqualTo(taskName.getValue());
+ then(respTaskType).isEqualTo(taskType.getValue());
+ then(TaskList.getTapasTaskList().getListOfTasks().getValue()).hasSize(1);
+
+ }
+
+ private ResponseEntity whenAddNewTaskToEmptyList(
+ Task.TaskName taskName,
+ Task.TaskType taskType,
+ Task.OriginalTaskUri originalTaskUri) throws JSONException {
+
+ TaskList.getTapasTaskList().getListOfTasks().getValue().clear();
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Content-Type", TaskJsonRepresentation.MEDIA_TYPE);
+
+ String jsonPayLoad = new JSONObject()
+ .put("taskName", taskName.getValue() )
+ .put("taskType", taskType.getValue())
+ .put("originalTaskUri",originalTaskUri.getValue())
+ .toString();
+
+ HttpEntity request = new HttpEntity<>(jsonPayLoad,headers);
+
+ return restTemplate.exchange(
+ "/tasks/",
+ HttpMethod.POST,
+ request,
+ Object.class
+ );
+
+ }
+
+}
diff --git a/tapas-tasks/src/test/java/ch/unisg/tapastasks/TapasTasksApplicationTests.java b/tapas-tasks/src/test/java/ch/unisg/tapastasks/TapasTasksApplicationTests.java
index b96cb8b..a343151 100644
--- a/tapas-tasks/src/test/java/ch/unisg/tapastasks/TapasTasksApplicationTests.java
+++ b/tapas-tasks/src/test/java/ch/unisg/tapastasks/TapasTasksApplicationTests.java
@@ -1,13 +1,18 @@
package ch.unisg.tapastasks;
+import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
+import ch.unisg.tapastasks.tasks.application.port.out.NewTaskAddedEventPort;
+import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
+import ch.unisg.tapastasks.tasks.application.service.AddNewTaskToTaskListService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class TapasTasksApplicationTests {
- @Test
+ @Test
void contextLoads() {
+
}
}
diff --git a/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/adapter/in/web/AddNewTaskToTaskListWebControllerTest.java b/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/adapter/in/web/AddNewTaskToTaskListWebControllerTest.java
new file mode 100644
index 0000000..d397908
--- /dev/null
+++ b/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/adapter/in/web/AddNewTaskToTaskListWebControllerTest.java
@@ -0,0 +1,68 @@
+package ch.unisg.tapastasks.tasks.adapter.in.web;
+
+import ch.unisg.tapastasks.tasks.adapter.in.formats.TaskJsonRepresentation;
+import ch.unisg.tapastasks.tasks.adapter.in.web.AddNewTaskToTaskListWebController;
+import ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb.TaskRepository;
+import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListCommand;
+import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListUseCase;
+import ch.unisg.tapastasks.tasks.domain.Task;
+import org.json.JSONObject;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.util.Optional;
+
+import static org.mockito.BDDMockito.eq;
+import static org.mockito.BDDMockito.then;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+
+@WebMvcTest(controllers = AddNewTaskToTaskListWebController.class)
+public class AddNewTaskToTaskListWebControllerTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @MockBean
+ private AddNewTaskToTaskListUseCase addNewTaskToTaskListUseCase;
+
+ @MockBean
+ TaskRepository taskRepository;
+
+ @Disabled
+ @Test
+ void testAddNewTaskToTaskList() throws Exception {
+
+ String taskName = "test-request";
+ String taskType = "test-request-type";
+ String originalTaskUri = "example.org";
+
+ String jsonPayLoad = new JSONObject()
+ .put("taskName", taskName )
+ .put("taskType", taskType)
+ .put("originalTaskUri",originalTaskUri)
+ .toString();
+
+ //This raises a NullPointerException since it tries to build the HTTP response with attributes from
+ //the domain object (created task), which is mocked --> we need System Tests here!
+ //See the buckpal example from the lecture for a working integration test for testing the web controller
+ mockMvc.perform(post("/tasks/")
+ .contentType(TaskJsonRepresentation.MEDIA_TYPE)
+ .content(jsonPayLoad))
+ .andExpect(status().isCreated());
+
+ then(addNewTaskToTaskListUseCase).should()
+ .addNewTaskToTaskList(eq(new AddNewTaskToTaskListCommand(
+ new Task.TaskName(taskName), new Task.TaskType(taskType),
+ Optional.of(new Task.OriginalTaskUri(originalTaskUri))
+ )));
+
+ }
+
+
+}
diff --git a/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskPersistenceAdapterTest.java b/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskPersistenceAdapterTest.java
new file mode 100644
index 0000000..ef1fa99
--- /dev/null
+++ b/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskPersistenceAdapterTest.java
@@ -0,0 +1,77 @@
+package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
+
+import ch.unisg.tapastasks.tasks.domain.Task;
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+@AutoConfigureDataMongo
+@Import({TaskPersistenceAdapter.class, TaskMapper.class})
+public class TaskPersistenceAdapterTest {
+
+ @Autowired
+ private TaskRepository taskRepository;
+
+ @Autowired
+ private TaskPersistenceAdapter adapterUnderTest;
+
+ @Test
+ void addsNewTask() {
+
+ String testTaskId = UUID.randomUUID().toString();
+ String testTaskName = "adds-persistence-task-name";
+ String testTaskType = "adds-persistence-task-type";
+ String testTaskOuri = "adds-persistence-test-task-ouri";
+ String testTaskStatus = Task.Status.OPEN.toString();
+ String testTaskListName = "tapas-tasks-tutors";
+
+
+ Task testTask = new Task(
+ new Task.TaskId(testTaskId),
+ new Task.TaskName(testTaskName),
+ new Task.TaskType(testTaskType),
+ new Task.OriginalTaskUri(testTaskOuri),
+ new Task.TaskStatus(Task.Status.valueOf(testTaskStatus))
+ );
+ adapterUnderTest.addTask(testTask);
+
+ MongoTaskDocument retrievedDoc = taskRepository.findByTaskId(testTaskId,testTaskListName);
+
+ assertThat(retrievedDoc.taskId).isEqualTo(testTaskId);
+ assertThat(retrievedDoc.taskName).isEqualTo(testTaskName);
+ assertThat(retrievedDoc.taskListName).isEqualTo(testTaskListName);
+
+ }
+
+ @Test
+ void retrievesTask() {
+
+ String testTaskId = UUID.randomUUID().toString();
+ String testTaskName = "reads-persistence-task-name";
+ String testTaskType = "reads-persistence-task-type";
+ String testTaskOuri = "reads-persistence-test-task-ouri";
+ String testTaskStatus = Task.Status.OPEN.toString();
+ String testTaskListName = "tapas-tasks-tutors";
+
+ MongoTaskDocument mongoTask = new MongoTaskDocument(testTaskId, testTaskName, testTaskType, testTaskOuri,
+ testTaskStatus, testTaskListName);
+ taskRepository.insert(mongoTask);
+
+ Task retrievedTask = adapterUnderTest.loadTask(new Task.TaskId(testTaskId),
+ new TaskList.TaskListName(testTaskListName));
+
+ assertThat(retrievedTask.getTaskName().getValue()).isEqualTo(testTaskName);
+ assertThat(retrievedTask.getTaskId().getValue()).isEqualTo(testTaskId);
+ assertThat(retrievedTask.getTaskStatus().getValue()).isEqualTo(Task.Status.valueOf(testTaskStatus));
+
+ }
+
+}
diff --git a/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListServiceTest.java b/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListServiceTest.java
new file mode 100644
index 0000000..10d21fd
--- /dev/null
+++ b/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListServiceTest.java
@@ -0,0 +1,63 @@
+package ch.unisg.tapastasks.tasks.application.service;
+
+import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListCommand;
+import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
+import ch.unisg.tapastasks.tasks.application.port.out.NewTaskAddedEventPort;
+import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
+import ch.unisg.tapastasks.tasks.domain.NewTaskAddedEvent;
+import ch.unisg.tapastasks.tasks.domain.Task;
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Optional;
+
+import static org.mockito.BDDMockito.*;
+import static org.assertj.core.api.Assertions.*;
+
+
+public class AddNewTaskToTaskListServiceTest {
+
+ private final AddTaskPort addTaskPort = Mockito.mock(AddTaskPort.class);
+ private final TaskListLock taskListLock = Mockito.mock(TaskListLock.class);
+ private final NewTaskAddedEventPort newTaskAddedEventPort = Mockito.mock(NewTaskAddedEventPort.class);
+ private final AddNewTaskToTaskListService addNewTaskToTaskListService = new AddNewTaskToTaskListService(
+ newTaskAddedEventPort, addTaskPort, taskListLock);
+
+ @Test
+ void addingSucceeds() {
+
+ Task newTask = givenATaskWithNameAndTypeAndURI(new Task.TaskName("test-task"),
+ new Task.TaskType("test-type"), Optional.of(new Task.OriginalTaskUri("example.org")));
+
+ TaskList taskList = givenAnEmptyTaskList(TaskList.getTapasTaskList());
+
+ AddNewTaskToTaskListCommand addNewTaskToTaskListCommand = new AddNewTaskToTaskListCommand(newTask.getTaskName(),
+ newTask.getTaskType(), Optional.ofNullable(newTask.getOriginalTaskUri()));
+
+ Task addedTask = addNewTaskToTaskListService.addNewTaskToTaskList(addNewTaskToTaskListCommand);
+
+ assertThat(addedTask).isNotNull();
+ assertThat(taskList.getListOfTasks().getValue()).hasSize(1);
+
+ then(taskListLock).should().lockTaskList(eq(TaskList.getTapasTaskList().getTaskListName()));
+ then(newTaskAddedEventPort).should(times(1))
+ .publishNewTaskAddedEvent(any(NewTaskAddedEvent.class));
+
+ }
+
+ private TaskList givenAnEmptyTaskList(TaskList taskList) {
+ taskList.getListOfTasks().getValue().clear();
+ return taskList;
+ }
+
+ private Task givenATaskWithNameAndTypeAndURI(Task.TaskName taskName, Task.TaskType taskType,
+ Optional originalTaskUri) {
+ Task task = Mockito.mock(Task.class);
+ given(task.getTaskName()).willReturn(taskName);
+ given(task.getTaskType()).willReturn(taskType);
+ given(task.getOriginalTaskUri()).willReturn(originalTaskUri.get());
+ return task;
+ }
+
+}
diff --git a/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/domain/TaskListTest.java b/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/domain/TaskListTest.java
new file mode 100644
index 0000000..05dc664
--- /dev/null
+++ b/tapas-tasks/src/test/java/ch/unisg/tapastasks/tasks/domain/TaskListTest.java
@@ -0,0 +1,49 @@
+package ch.unisg.tapastasks.tasks.domain;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.*;
+
+
+public class TaskListTest {
+
+ @Test
+ void addNewTaskToTaskListSuccess() {
+ TaskList taskList = TaskList.getTapasTaskList();
+ taskList.getListOfTasks().getValue().clear();
+ Task newTask = taskList.addNewTaskWithNameAndType(new Task.TaskName("My-Test-Task"),
+ new Task.TaskType("My-Test-Type"));
+
+ assertThat(newTask.getTaskName().getValue()).isEqualTo("My-Test-Task");
+ assertThat(taskList.getListOfTasks().getValue()).hasSize(1);
+ assertThat(taskList.getListOfTasks().getValue().get(0)).isEqualTo(newTask);
+
+ }
+
+ @Test
+ void retrieveTaskSuccess() {
+ TaskList taskList = TaskList.getTapasTaskList();
+ Task newTask = taskList.addNewTaskWithNameAndType(new Task.TaskName("My-Test-Task2"),
+ new Task.TaskType("My-Test-Type2"));
+
+ Task retrievedTask = taskList.retrieveTaskById(newTask.getTaskId()).get();
+
+ assertThat(retrievedTask).isEqualTo(newTask);
+
+ }
+
+ @Test
+ void retrieveTaskFailure() {
+ TaskList taskList = TaskList.getTapasTaskList();
+ Task newTask = taskList.addNewTaskWithNameAndType(new Task.TaskName("My-Test-Task3"),
+ new Task.TaskType("My-Test-Type3"));
+
+ Task.TaskId fakeId = new Task.TaskId("fake-id");
+
+ Optional retrievedTask = taskList.retrieveTaskById(fakeId);
+
+ assertThat(retrievedTask.isPresent()).isFalse();
+ }
+}
diff --git a/tapas-tasks/src/test/resources/application-test.properties b/tapas-tasks/src/test/resources/application-test.properties
new file mode 100644
index 0000000..e45b53d
--- /dev/null
+++ b/tapas-tasks/src/test/resources/application-test.properties
@@ -0,0 +1,2 @@
+spring.data.mongodb.uri=mongodb://127.0.0.1:27017
+spring.data.mongodb.database=tapas-tasks