diff --git a/doc/Classdiagramm/pm3-dcd.png b/doc/Classdiagramm/pm3-dcd.png
new file mode 100644
index 0000000..a94ebe3
Binary files /dev/null and b/doc/Classdiagramm/pm3-dcd.png differ
diff --git a/doc/Classdiagramm/pm3-dcd.uxf b/doc/Classdiagramm/pm3-dcd.uxf
new file mode 100644
index 0000000..5d662bd
--- /dev/null
+++ b/doc/Classdiagramm/pm3-dcd.uxf
@@ -0,0 +1,144 @@
+10UMLClass48236010030MainUMLClass1082380370100<<Interface>>
+PlantDatabase
+--
++ getPlantList(zone: HardinessZone): List<Plant>
++ getPlantById(zone: HardinessZone id: long): Optional<Plant>UMLClass67271022040TaskListControllerUMLClass1132110300180<<Record>>
+Plant
+--
++ id: long
++ name: String
++ description: String
++ spacing: int
++ lifecycle: List<GrowthPhase>
+--
++ calculateStartDate(harvestDate: Date): Date
++ generateTasks()
+UMLClass942710210120TaskListModel
+--
+- tasks: ListProperty<Task>
+- taskDb: TaskDatabase
+--
++ getTask(id: long): Optional<Task>
++ saveTask(task: Task)
++ removeTask(task: Task)UMLClass322690250120GardenPlanModel
+--
+- tasks: ListProperty<Crop>
+- gardenPlan: GardenPlan
+--
++ plantAsCrop(planting: UserPlanting)
++ removePlanting(planting: UserPlanting)UMLClass7268018080GardenPlanControllerUMLClass1362830490240Task
+--
++ id: long
++ name: String {readOnly}
++ description: String {readOnly}
++ startDate: Date {readOnly}
++ isOptional: boolean {readOnly}
+- interval: int
+- endDate: Date
+--
++ Task(name: String, description: String, startDate: String, isReadOnly: boolean): Task
++ withInterval(interval: int): Task
++ withEndDate(endDate: Date): Task
++ withId(id: long): Task
+--
++ getInterval(): Optional<int>
++ getEndDate(): Optional<Date>UMLClass902930280140<<Interface>>
+TaskDatabase
+--
++ getTaskList(start: Date, end: Date): List<Task>
++ saveTask(Task task) throws ??Exception
++ removeTask(Task task) throws ??ExceptionUMLClass44244017080MainWindowControllerUMLClass962131021070NotificationService
+--
+- taskDb: TaskDatabase
+--
++ tick()UMLClass1412131024090<<Interface>>
+WeatherProvider
+--
++ getWeatherForecast: WeatherForecastUMLClass13921150400100WeatherService
+--
+- weatherPovider: WeatherProvider
+- taskDb: TaskDatabase
+--
++ WeatherService(provider: WeatherProvider, taskDb: TaskDatabase)
+- updateTasks()UMLClass1502110260150<<Record>>
+GrowthPhase
+--
++ startDate: MonthDay
++ endDate: MonthDay
++ type: GrowthPhaseType
+
+Relation142218010040lt=<->>>>>
+m1=*
+m2=180;10;10;10Text15213019070Note:
+--
+{final, readOnly} omitted on public data fields in <<Record>> Entities for clarity
+style=wordwrapRelation117297021050lt=<.
+m1=*
+m2=1
+returns >190;20;10;20Relation1032820140130lt=<-
+m1=1 external database
+m2=1 internal list
+accesses10;110;10;10UMLClass3221120240150Crop
+--
+- cropId: Long
++ plantId: long {final, readOnly}
++ startDate: LocalDate {final, readOnly}
++ area: Double
+--
++ withId(long): Crop
++ withArea(double): Crop
+--
++ getCropId(): Optional<Long>Relation126228070120lt=<.
+m1=*
+m2=1
+returns10;10;10;100UMLClass1632290130100<<Enumeration>>
+GrowthPhaseType
+--
+SOW
+PLANT
+HARVESTRelation158225070120lt=<->>>>
+m1=1
+m2=*50;90;10;90;10;10Relation8827208030lt=<->>>>60;10;10;10Relation4321020110120lt=<.
+m1=*
+m2=1
+returns / saves10;100;10;10Relation24271010030lt=<->>>>80;10;10;10UMLClass302890280140<<Interface>>
+GardenPlan
+--
++ getPlantings(): List<UserPlanting>
++ addPlanting(plantId: long, startDate)
++ savePlanting(planting: UserPlanting)
+Relation42280080110lt=<.
+m1=1
+m2=1
+accesses10;90;10;10Relation152212403090lt=<.10;70;10;10Relation1062106080270lt=<-
+m1=1
+m2=1
+accesses10;10;10;250Relation11421060270170lt=<-
+m1=1
+m2=1
+adds Tasks10;10;10;140;250;140UMLClass822114020050JsonTaskDatabaseRelation912106030100lt=<<-10;10;10;80UMLClass156243020050JsonPlantDatabaseRelation144244014030lt=<<-10;10;120;10UMLClass3293020050JsonGardenPlanRelation22294010030lt=<<-80;10;10;10Relation582510220220lt=<.200;200;10;10Relation172510340190lt=<.10;170;320;10UMLClass58225016060PlantListControllerRelation5223803080lt=<.10;60;10;10Relation60230080210lt=<.60;10;10;190UMLClass73239016060PlantListModelRelation73228090130lt=<.70;110;10;10Relation88240022070lt=<-
+m1=1\nexternal\ndatabase
+m2=1\ninternal\nlist
+accesses >200;20;10;20Relation582019050lt=-
+m1=0..n
+m2=0..1
+teaches to >10;20;170;20UMLClass1412530420200TaskTemplate
+--
++ name: String {readOnly}
++ description: String {readOnly}
++ relativeStartDate: int {readOnly}
+- interval: Integer
+- relativeEndDate: Integer
+- isOptional: boolean
+--
++ Task(name: String, description: String, startDate: String): TaskTemplate
++ setRelativeEndDate(relativeEndDate: int)
++ setInterval(interval: Integer)
++ generateTask(realStartDate: LocalDate): Task
+Relation1752180130490lt=<->>>>>
+m1=*
+m2=180;460;110;460;110;10;10;10Relation160272090130lt=<.
+m1=*
+m2=1
+generates10;110;10;10Relation56277040040lt=<-
+delegates generating Tasks380;20;10;20
\ No newline at end of file
diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java
index 5a25a0a..3100301 100644
--- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java
+++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java
@@ -20,7 +20,7 @@ import java.util.Map;
import java.util.Optional;
public class JsonGardenPlan implements GardenPlan {
- private final URL dataSource = getClass().getResource("user-crops.json");
+ private final URL dataSource;
private IdProvider idProvider;
private Map cropMap = Collections.emptyMap();
@@ -40,7 +40,21 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
- * @see GardenPlan#getCrops()
+ * Default constructor
+ */
+ public JsonGardenPlan() {
+ this.dataSource = getClass().getResource("user-crops.json");
+ }
+
+ /**
+ * Constructor to use a specified {@link URL} as a {@link #dataSource}
+ * @param dataSource A {@link URL} to the file to be used as a data source
+ */
+ public JsonGardenPlan(URL dataSource) {
+ this.dataSource = dataSource;
+ }
+ /**
+ * {@inheritDoc}
*/
@Override
public List getCrops() throws IOException {
@@ -51,7 +65,7 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
- * @see GardenPlan#getCropById(long)
+ * {@inheritDoc}
*/
@Override
public Optional getCropById(long id) throws IOException {
@@ -62,7 +76,7 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
- * @see GardenPlan#saveCrop(Crop)
+ * {@inheritDoc}
*
* Saves a crop to the database.
* If no {@link Crop#cropId} is set, one will be generated and
@@ -79,7 +93,7 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
- * @see GardenPlan#removeCrop(Crop)
+ * {@inheritDoc}
*/
@Override
public void removeCrop(Crop crop) throws IOException {
diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java
index b7c982a..f09d90e 100644
--- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java
+++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java
@@ -89,15 +89,16 @@ public class JsonPlantDatabase implements PlantDatabase {
List result;
result = mapper.readerForListOf(Plant.class).readValue(dataSource);
- for (Plant plant : result) {
- plant.inZone(currentZone);
- }
-
- // Turn list into a HashMap with structure id => Plant
plantMap = result.stream()
+ // Remove plants not in the current zone
+ .filter(plant -> {
+ plant.inZone(currentZone);
+ return !plant.lifecycle().isEmpty();
+ })
+ // Create Hashmap from results
.collect(HashMap::new,
(res, plant) -> res.put(plant.id(), plant),
- (existing, replacement) -> { });
+ (existing, replacement) -> {});
}
}
}
diff --git a/src/main/java/ch/zhaw/gartenverwaltung/taskList/TaskListModel.java b/src/main/java/ch/zhaw/gartenverwaltung/taskList/TaskListModel.java
index 4f9384d..cb766be 100644
--- a/src/main/java/ch/zhaw/gartenverwaltung/taskList/TaskListModel.java
+++ b/src/main/java/ch/zhaw/gartenverwaltung/taskList/TaskListModel.java
@@ -9,8 +9,10 @@ import ch.zhaw.gartenverwaltung.types.*;
import java.io.IOException;
import java.time.LocalDate;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
public class TaskListModel {
@@ -77,6 +79,10 @@ public class TaskListModel {
taskDatabase.removeTask(task);
}
+ private List filterListByCrop(List taskList, Long cropId) {
+ return taskList.stream().filter(task -> task.getCropId() == cropId).collect(Collectors.toList());
+ }
+
/**
* Method to get all Tasks
* @return List of all Tasks
@@ -86,6 +92,15 @@ public class TaskListModel {
return getFilteredTaskList(LocalDate.MIN, LocalDate.MAX);
}
+ /**
+ * Method to get all Tasks for specific Crop
+ * @return List of all Tasks for with given CropID
+ * @throws IOException If the database cannot be accessed
+ */
+ public List getTaskListForCrop(Long cropId) throws IOException {
+ return filterListByCrop(getTaskList(), cropId);
+ }
+
/**
* Method to get all Tasks which are today or in future
* @return List of Tasks
@@ -95,6 +110,15 @@ public class TaskListModel {
return getFilteredTaskList(LocalDate.now(), LocalDate.MAX);
}
+ /**
+ * Method to get all Tasks which are today or in future for specific Crop
+ * @return List of Tasks with given crop ID
+ * @throws IOException If the database cannot be accessed
+ */
+ public List getFutureTasksForCrop(Long cropId) throws IOException {
+ return filterListByCrop(getFutureTasks(), cropId);
+ }
+
/**
* Method to get all Tasks which are today or in past
* @return List of Tasks
@@ -105,17 +129,40 @@ public class TaskListModel {
}
/**
- * Method to get an Array of 7 Tasklists for the next 7 days. Index 0 is Tasklist for Today.
- * @return Array with length 7 (List[])
+ * Method to get all Tasks which are today or in past for specifc crop
+ * @return List of Tasks with given grop id
* @throws IOException If the database cannot be accessed
*/
- public List[] getTasksUpcomingWeek() throws IOException {
- List[] listArray = new List[7];
+ public List getPastTasksForCrop(Long cropId) throws IOException {
+ return filterListByCrop(getPastTasks(), cropId);
+ }
+
+ /**
+ * Method to get an List of 7 Tasklists for the next 7 days. Index 0 is Tasklist for Today.
+ * @return List with length 7 (List>)
+ * @throws IOException If the database cannot be accessed
+ */
+ public List> getTasksUpcomingWeek() throws IOException {
+ List> dayTaskList = new ArrayList<>();
for(int i = 0; i < 7; i++) {
LocalDate date = LocalDate.now().plusDays(i);
- listArray[i] = taskDatabase.getTaskList(date, date);
+ dayTaskList.add(taskDatabase.getTaskList(date, date));
}
- return listArray;
+ return dayTaskList;
+ }
+
+ /**
+ * Method to get an List of 7 Tasklists for the next 7 days. (Filtered Index 0 is Tasklist for Today.
+ * @return List with length 7 (List>)
+ * @throws IOException If the database cannot be accessed
+ */
+ public List> getTasksUpcomingWeekForCrop(Long cropId) throws IOException {
+ List> dayTaskList = new ArrayList<>();
+ for(int i = 0; i < 7; i++) {
+ LocalDate date = LocalDate.now().plusDays(i);
+ dayTaskList.add(filterListByCrop(taskDatabase.getTaskList(date, date), cropId));
+ }
+ return dayTaskList;
}
/**
diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java
index fbd6286..e8f539f 100644
--- a/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java
+++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java
@@ -1,6 +1,7 @@
package ch.zhaw.gartenverwaltung.types;
import java.time.LocalDate;
+import java.util.Objects;
import java.util.Optional;
public class Crop {
@@ -41,6 +42,24 @@ public class Crop {
public LocalDate getStartDate() { return startDate; }
public double getArea() { return area; }
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if (other instanceof Crop otherCrop) {
+ return Objects.equals(this.cropId, otherCrop.cropId) &&
+ plantId == otherCrop.plantId &&
+ startDate != null && startDate.equals(otherCrop.startDate) &&
+ area == otherCrop.area;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int startCode = startDate != null ? startDate.hashCode() : 0;
+ return (int) plantId ^ (startCode << 16);
+ }
+
@Override
public String toString() {
return String.format("Crop [ cropId: %d, plantId: %d, startDate: %s, area: %f ]",
diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java
index 545864d..02d6480 100644
--- a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java
+++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java
@@ -21,11 +21,11 @@ public class Task {
* default constructor
* (used by Json deserializer)
*/
- public Task(){
+ public Task(long cropId){
name= "";
description= "";
startDate = LocalDate.now();
- // this.cropId = cropId;
+ this.cropId = cropId;
}
public Task(String name, String description, LocalDate startDate, long cropId) {
diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json
index 5a23d09..73bd7ab 100644
--- a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json
+++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json
@@ -31,7 +31,7 @@
"name": "Germinate",
"relativeStartDate": -14,
"relativeEndDate": null,
- "description": "\"Take an egg carton and fill it with soil. Put the seedling deep enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"",
+ "description": "Take an egg carton and fill it with soil. Put the seedling deep enough so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.",
"interval": null,
"isOptional": false
}
@@ -53,7 +53,7 @@
"name": "hilling",
"relativeStartDate": 0,
"relativeEndDate": null,
- "description": "\"When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.\"",
+ "description": "When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.",
"interval": 21,
"isOptional": false
}
diff --git a/src/test/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlanTest.java b/src/test/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlanTest.java
new file mode 100644
index 0000000..ea22b3b
--- /dev/null
+++ b/src/test/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlanTest.java
@@ -0,0 +1,105 @@
+package ch.zhaw.gartenverwaltung.io;
+
+import ch.zhaw.gartenverwaltung.types.Crop;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class JsonGardenPlanTest {
+ private GardenPlan testDatabase;
+ private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ /**
+ * Files to isolate the test-units
+ */
+ private final URL dbDataSource = this.getClass().getResource("user-crops.json");
+ private final URL testFile = this.getClass().getResource("test-user-crops.json");
+
+ @BeforeEach
+ void connectToDb() throws URISyntaxException, IOException {
+ assertNotNull(testFile);
+ assertNotNull(dbDataSource);
+ Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
+ testDatabase = new JsonGardenPlan(dbDataSource);
+ }
+
+
+ @Test
+ @DisplayName("Check if results are retrieved completely.")
+ void getCropsNotEmpty() {
+ List testList;
+ try {
+ testList = testDatabase.getCrops();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ Assertions.assertEquals(3, testList.size());
+
+ List plantIds = testList.stream().map(Crop::getPlantId).collect(Collectors.toList());
+ List expected = Arrays.asList(1L, 1L, 0L);
+ Assertions.assertEquals(expected, plantIds);
+ }
+
+ @Test
+ @DisplayName("Check whether single access works.")
+ void getCropById() throws IOException {
+ Optional testCrop = testDatabase.getCropById(1);
+ assertTrue(testCrop.isPresent());
+ Assertions.assertEquals(1, testCrop.get().getPlantId());
+ }
+
+
+ @Test
+ @DisplayName("Check for a nonexisting crop.")
+ void getCropByIdMustFail() throws IOException {
+ Optional testCrop = testDatabase.getCropById(99);
+ Assertions.assertFalse(testCrop.isPresent());
+ }
+
+ @Test
+ @DisplayName("Add new Crop.")
+ void addNewCrop() {
+ Crop crop = new Crop(3L, LocalDate.parse("2023-02-22", formatter));
+ try {
+ testDatabase.saveCrop(crop);
+ assertTrue(crop.getCropId().isPresent());
+ Optional testCrop = testDatabase.getCropById(crop.getCropId().get());
+
+ assertTrue(testCrop.isPresent());
+ Assertions.assertEquals(crop, testCrop.get());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ @DisplayName("Remove crop")
+ void removeCrop(){
+ try {
+ Optional crop = testDatabase.getCropById(2L);
+ Assertions.assertTrue(crop.isPresent());
+ testDatabase.removeCrop(crop.get());
+ crop = testDatabase.getCropById(2L);
+ Assertions.assertFalse(crop.isPresent());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
+
diff --git a/src/test/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabaseTest.java b/src/test/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabaseTest.java
index a21b440..b8bbeb3 100644
--- a/src/test/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabaseTest.java
+++ b/src/test/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabaseTest.java
@@ -8,7 +8,6 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.IOException;
-import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@@ -16,7 +15,6 @@ import java.util.stream.Collectors;
public class JsonPlantDatabaseTest {
PlantDatabase testDatabase;
- SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
@BeforeEach
void connectToDb() {
@@ -26,7 +24,7 @@ public class JsonPlantDatabaseTest {
@Test
@DisplayName("Check if results are retrieved completely")
- void getPlantList() {
+ void getPlantListNotEmpty() {
List testList;
try {
testList = testDatabase.getPlantList(HardinessZone.ZONE_8A);
@@ -40,12 +38,26 @@ public class JsonPlantDatabaseTest {
Assertions.assertEquals(expected,names);
}
+ @Test
+ @DisplayName("Check if results are retrieved correctly when empty")
+ void getPlantListEmpty() {
+ List testList;
+ try {
+ testList = testDatabase.getPlantList(HardinessZone.ZONE_1A);
+ } catch (IOException | HardinessZoneNotSetException e) {
+ throw new RuntimeException(e);
+ }
+ Assertions.assertEquals(0, testList.size());
+
+
+ }
+
@Test
@DisplayName("Check whether single access works.")
- void getPlantById() {
+ void getPlantByIdAndZone() {
Optional testPlant;
try {
- testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A,1);
+ testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 1);
} catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e);
}
@@ -53,12 +65,23 @@ public class JsonPlantDatabaseTest {
Assertions.assertEquals("Early Carrot", testPlant.get().name());
}
+ @Test
+ @DisplayName("Check whether single access respects zone correctly.")
+ void getPlantByIdAndWrongZone() throws HardinessZoneNotSetException, IOException {
+ Optional testPlant = testDatabase.getPlantById(HardinessZone.ZONE_1A, 1);
+ Assertions.assertFalse(testPlant.isPresent());
+ testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 1);
+ Assertions.assertTrue(testPlant.isPresent());
+
+ Assertions.assertEquals("Early Carrot", testPlant.get().name());
+ }
+
@Test
@DisplayName("Check for a nonexisting plant.")
void getPlantByIdMustFail() {
Optional testPlant;
try {
- testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A,99);
+ testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 99);
} catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e);
}
diff --git a/src/test/java/ch/zhaw/gartenverwaltung/taskList/TaskListModelTest.java b/src/test/java/ch/zhaw/gartenverwaltung/taskList/TaskListModelTest.java
new file mode 100644
index 0000000..9caf65a
--- /dev/null
+++ b/src/test/java/ch/zhaw/gartenverwaltung/taskList/TaskListModelTest.java
@@ -0,0 +1,158 @@
+package ch.zhaw.gartenverwaltung.taskList;
+
+import ch.zhaw.gartenverwaltung.io.*;
+import ch.zhaw.gartenverwaltung.types.HardinessZone;
+import ch.zhaw.gartenverwaltung.types.Plant;
+import ch.zhaw.gartenverwaltung.types.Task;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+class TaskListModelTest {
+ TaskDatabase taskDatabase;
+ PlantDatabase plantDatabase;
+ List exampleTaskList;
+ Map examplePlantMap;
+ TaskListModel model;
+
+ @BeforeEach
+ void setUp() throws IOException {
+ createExampleTaskList();
+ taskDatabase = mockTaskDatabase(exampleTaskList);
+ plantDatabase = mockPlantDatabase(examplePlantMap);
+ model = new TaskListModel(taskDatabase, plantDatabase);
+ }
+
+ private TaskDatabase mockTaskDatabase(List exampleTaskList) throws IOException {
+ TaskDatabase taskDatabase = mock(JsonTaskDatabase.class);
+ when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList);
+
+ when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4)));
+
+ List pastTasks = new ArrayList<>();
+ pastTasks.add(exampleTaskList.get(0));
+ pastTasks.add(exampleTaskList.get(2));
+ pastTasks.add(exampleTaskList.get(4));
+ when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.now())).thenReturn(pastTasks);
+
+
+ when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.now())).thenReturn(List.of(exampleTaskList.get(2)));
+ when(taskDatabase.getTaskList(LocalDate.now().plusDays(1L), LocalDate.now().plusDays(1L))).thenReturn(List.of(exampleTaskList.get(1)));
+ when(taskDatabase.getTaskList(LocalDate.now().plusDays(2L), LocalDate.now().plusDays(2L))).thenReturn(List.of());
+ when(taskDatabase.getTaskList(LocalDate.now().plusDays(3L), LocalDate.now().plusDays(3L))).thenReturn(List.of());
+ when(taskDatabase.getTaskList(LocalDate.now().plusDays(4L), LocalDate.now().plusDays(4L))).thenReturn(List.of());
+ when(taskDatabase.getTaskList(LocalDate.now().plusDays(5L), LocalDate.now().plusDays(5L))).thenReturn(List.of());
+ when(taskDatabase.getTaskList(LocalDate.now().plusDays(6L), LocalDate.now().plusDays(6L))).thenReturn(List.of());
+
+ return taskDatabase;
+ }
+
+ private PlantDatabase mockPlantDatabase(Map examplePlantMap) {
+ return new PlantDatabase() {
+ @Override
+ public List getPlantList(HardinessZone zone) {
+ return null;
+ }
+
+ @Override
+ public Optional getPlantById(HardinessZone zone, long id) {
+ return Optional.ofNullable(examplePlantMap.get(id));
+ }
+ };
+ }
+
+ void createExampleTaskList() {
+ exampleTaskList = new ArrayList<>();
+ exampleTaskList.add(new Task("name", "description", LocalDate.now().minusDays(1), 1L));
+ exampleTaskList.add(new Task("name", "description", LocalDate.now().plusDays(1), 2L));
+ exampleTaskList.add(new Task("name", "description", LocalDate.now(), 1L));
+ exampleTaskList.add(new Task("name", "description", LocalDate.of(9019, 5, 5), 1L));
+ exampleTaskList.add(new Task("name", "description", LocalDate.of(2019, 5, 5), 2L));
+ }
+
+ void createExamplePlantMap() {
+ examplePlantMap = new HashMap<>();
+ }
+
+
+
+ @Test
+ void addTask() throws IOException {
+ Task taskToAdd = new Task("name", "description", LocalDate.now(), 1L);
+ model.addTask(taskToAdd);
+ verify(taskDatabase, times(1)).saveTask(taskToAdd);
+ }
+
+ @Test
+ void removeTask() throws IOException {
+ Task taskToRemove = new Task("name", "description", LocalDate.now(), 1L);
+ model.removeTask(taskToRemove);
+ verify(taskDatabase, times(1)).removeTask(taskToRemove);
+ }
+
+ @Test
+ void getTaskList() throws IOException {
+ List listToCheck = model.getTaskList();
+ assertEquals(5, listToCheck.size());
+ assertEquals(exampleTaskList.get(4), listToCheck.get(0));
+ assertEquals(exampleTaskList.get(0), listToCheck.get(1));
+ assertEquals(exampleTaskList.get(2), listToCheck.get(2));
+ assertEquals(exampleTaskList.get(1), listToCheck.get(3));
+ assertEquals(exampleTaskList.get(3), listToCheck.get(4));
+ }
+
+ @Test
+ void getFutureTasks() throws IOException {
+ List listToCheck = model.getFutureTasks();
+ assertEquals(3, listToCheck.size());
+ assertEquals(exampleTaskList.get(2), listToCheck.get(0));
+ assertEquals(exampleTaskList.get(1), listToCheck.get(1));
+ assertEquals(exampleTaskList.get(3), listToCheck.get(2));
+ }
+
+ @Test
+ void getPastTasks() throws IOException {
+ List listToCheck = model.getPastTasks();
+ assertEquals(3, listToCheck.size());
+ assertEquals(exampleTaskList.get(4), listToCheck.get(0));
+ assertEquals(exampleTaskList.get(0), listToCheck.get(1));
+ assertEquals(exampleTaskList.get(2), listToCheck.get(2));
+ }
+
+ @Test
+ void getTasksUpcomingWeek() throws IOException {
+ List> dayList = model.getTasksUpcomingWeek();
+ assertEquals(7, dayList.size());
+
+ //Check day 0
+ assertEquals(1, dayList.get(0).size());
+ assertEquals(exampleTaskList.get(2), dayList.get(0).get(0));
+
+ //Check day 1
+ assertEquals(1, dayList.get(1).size());
+ assertEquals(exampleTaskList.get(1), dayList.get(1).get(0));
+
+ //Check day 2
+ assertEquals(0, dayList.get(2).size());
+ //Check day 3
+ assertEquals(0, dayList.get(3).size());
+ //Check day 4
+ assertEquals(0, dayList.get(4).size());
+ //Check day 5
+ assertEquals(0, dayList.get(5).size());
+ //Check day 6
+ assertEquals(0, dayList.get(6).size());
+ }
+
+ @Test
+ void removeTasksForCrop() throws IOException {
+ model.removeTasksForCrop(1L);
+ verify(taskDatabase, times(1)).removeTasksForCrop(1L);
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/ch/zhaw/gartenverwaltung/io/test-user-crops.json b/src/test/resources/ch/zhaw/gartenverwaltung/io/test-user-crops.json
new file mode 100644
index 0000000..ebd1d2d
--- /dev/null
+++ b/src/test/resources/ch/zhaw/gartenverwaltung/io/test-user-crops.json
@@ -0,0 +1,20 @@
+[
+ {
+ "cropId": 0,
+ "plantId": 1,
+ "startDate": "2023-02-25",
+ "area": 0.5
+ },
+ {
+ "cropId": 1,
+ "plantId": 1,
+ "startDate": "2023-03-01",
+ "area": 0.5
+ },
+ {
+ "cropId": 2,
+ "plantId": 0,
+ "startDate": "2023-03-25",
+ "area": 1.5
+ }
+]
\ No newline at end of file
diff --git a/src/test/resources/ch/zhaw/gartenverwaltung/io/user-crops.json b/src/test/resources/ch/zhaw/gartenverwaltung/io/user-crops.json
new file mode 100644
index 0000000..32960f8
--- /dev/null
+++ b/src/test/resources/ch/zhaw/gartenverwaltung/io/user-crops.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file