Skip to content
Snippets Groups Projects
Commit a12c80be authored by Karsch Lukas's avatar Karsch Lukas
Browse files

Merge remote-tracking branch 'origin/59-integration-tests' into 59-integration-tests

parents 3f85b883 2335eeb7
No related branches found
No related tags found
1 merge request!50Resolve "Integration tests"
Showing
with 542 additions and 213 deletions
...@@ -12,6 +12,14 @@ maven icon. Then, run the "GrowBrosApplication". The API can be accessed at http ...@@ -12,6 +12,14 @@ maven icon. Then, run the "GrowBrosApplication". The API can be accessed at http
Go into the "growbros-frontend" folder and open it in VSCode. Run "npm install" (Node.js must be installed). Then, Go into the "growbros-frontend" folder and open it in VSCode. Run "npm install" (Node.js must be installed). Then,
type "npm run dev". type "npm run dev".
### Documentation
How to run our project is documented in this README.
The different features are listed in the CHANGELOG.
Our architectural decisions are documented as ARDs in the Gitlab Wiki as well as our reflections.
### Links ### Links
* install node.js https://nodejs.org/en * install node.js https://nodejs.org/en
* read our Wiki: https://gitlab.mi.hdm-stuttgart.de/tomato/growbros/-/wikis/home
<!doctype html> <!doctype html>
<html lang="en"> <html lang="de">
<head> <head>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<link rel="icon" type="image/svg+xml" href="/vite.svg"/> <link rel="icon" type="image/png" href="/recources/growbros_logo.png"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Vite + React + TS</title> <title>GrowBros - Manage your garden</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
......
growbros-frontend/public/recources/growbros_logo.png

48.4 KiB

...@@ -5,8 +5,10 @@ const bc = new BackendConnectorImpl("http://localhost:8080/api/v1"); ...@@ -5,8 +5,10 @@ const bc = new BackendConnectorImpl("http://localhost:8080/api/v1");
export function BackendConnectorTestInterface() { export function BackendConnectorTestInterface() {
return <div style={{padding: "1rem"}}> return <div style={{padding: "1rem"}}>
<h2>Backend connector test</h2> <h2>Backend connector test</h2>
<p>Achtung: das ist / war ausschließlich zum Testen von meinen geschriebenen fetch-methoden. Benutzt das auf <p><b>Achtung:</b> das ist / war ausschließlich zum Testen von meinen geschriebenen fetch-methoden. Benutzt das
keinen Fall in euren components... lg lukas</p> auf
keinen Fall in euren components... lg lukas
</p>
<h3>Plants controller</h3> <h3>Plants controller</h3>
<ul style={{display: "block", listStyleType: "initial"}}> <ul style={{display: "block", listStyleType: "initial"}}>
<li onClick={async () => console.log(await bc.getSinglePlant(1101))}>Get single plant</li> <li onClick={async () => console.log(await bc.getSinglePlant(1101))}>Get single plant</li>
...@@ -19,7 +21,7 @@ export function BackendConnectorTestInterface() { ...@@ -19,7 +21,7 @@ export function BackendConnectorTestInterface() {
<li onClick={async () => console.log(await bc.getGardenEntries())}>Get garden</li> <li onClick={async () => console.log(await bc.getGardenEntries())}>Get garden</li>
<li onClick={async () => console.log(await bc.addToGarden(1000))}>Add plant to garden</li> <li onClick={async () => console.log(await bc.addToGarden(1000))}>Add plant to garden</li>
<li onClick={async () => console.log(await bc.getGardenSize())}>garden size</li> <li onClick={async () => console.log(await bc.getGardenSize())}>garden size</li>
<li onClick={async () => console.log(await bc.removeEntryFromGarden(3))}>remove entry</li> <li onClick={async () => console.log(await bc.removeFromGarden(3))}>remove entry</li>
<li onClick={async () => console.log(await bc.clearGarden())}>clear garden</li> <li onClick={async () => console.log(await bc.clearGarden())}>clear garden</li>
</ul> </ul>
<h3>Wishlist controller</h3> <h3>Wishlist controller</h3>
......
import { useState } from "react"; import {useState} from "react";
import "../stylesheets/Home.css"; import "../stylesheets/Home.css";
import { NavLink } from "react-router-dom"; import {NavLink} from "react-router-dom";
import { checkJwtStatus } from "../jwt/Cookies"; import {checkJwtStatus} from "../jwt/Cookies";
function Home() { function Home() {
return ( return (
<main> <main>
<div className="hero"> <div className="hero">
<img <img
src="../../public/recources/images/Vegetables.jpeg" src="../../public/recources/images/Vegetables.jpeg"
className="mainPicture" className="mainPicture"
></img> alt="fresh plants"/>
<div className="descriptionAside"> <div className="descriptionAside">
<h1 className="header1">GrowBros</h1> <h1 className="header1">GrowBros</h1>
<h2 className="header2">Keep your plants alive</h2> <h2 className="header2">Keep your plants alive</h2>
<p className="text"> <p>
"Mit GrowBros wird Gärtnern zum Kinderspiel! Finde die perfekten "Mit GrowBros wird Gärtnern zum Kinderspiel! Finde die perfekten
Pflanzen für deinen Garten, erhalte nützliche Informationen und Pflanzen für deinen Garten, erhalte nützliche Informationen und
verwalte sie mühelos. Starte noch heute und lass deinen Garten in verwalte sie mühelos. Starte noch heute und lass deinen Garten in
voller Blüte erstrahlen!" voller Blüte erstrahlen!"
</p> </p>
<StartNowLink></StartNowLink> <StartNowLink/>
</div> </div>
</div> </div>
<div className="secondDiv"> <div className="secondDiv">
Fange noch heute an dir deinen Traumgarten zusammenzustellen und ernte Fange noch heute an, dir deinen Traumgarten zusammenzustellen und ernte
bald die Früchte deiner Arbeit. bald die Früchte deiner Arbeit.
</div> </div>
<div style={{ display: "flex" }}> <div style={{display: "flex"}}>
<div className="smallDiv" style={{ width: "60%" }}> <div className="smallDiv" style={{width: "60%"}}>
<h2>Unsere Vorteile...</h2> <h2>Unsere Vorteile...</h2>
<p> <p>
Entdecke neue Pflanzen und erhalte wertvolle Informationen für Entdecke neue Pflanzen und erhalte wertvolle Informationen für
deinen Garten. Erfasse sie mühelos und erhalte Erinnerungen für Deinen Garten. Erfasse sie mühelos und erhalte Erinnerungen für
Pflege und Ernte. Pflege und Ernte.
</p> </p>
</div> </div>
<img <img
className="smallPicture" className="smallPicture"
src="../../public/recources/images/Broccoli.webp" src="../../public/recources/images/Broccoli.webp"
></img> alt="broccoli"></img>
</div> </div>
<div style={{ display: "flex" }}> <div style={{display: "flex"}}>
<img <img
className="smallPicture" className="smallPicture"
src="../../public/recources/images/Red_Vegetable.webp" src="../../public/recources/images/Red_Vegetable.webp"
></img> alt="red vegetables"></img>
<div className="smallDiv"> <div className="smallDiv">
<p> <p>
Organisiere deinen Garten besser. Verfolge den Fortschritt deiner Organisiere deinen Garten besser. Verfolge den Fortschritt deiner
Pflanzen und erhalte Anleitungen, um sie gesund und glücklich zu Pflanzen und erhalte Anleitungen, um sie gesund und glücklich zu
halten. halten.
</p> </p>
</div> </div>
</div> </div>
<div style={{ display: "flex" }}> <div style={{display: "flex"}}>
<div className="smallDiv"> <div className="smallDiv">
<p> <p>
Maximiere deine Ernte. Unsere App hilft dir, den optimalen Zeitpunkt Maximiere Deine Ernte. Unsere App hilft dir, den optimalen Zeitpunkt
für die Ernte deiner Pflanzen zu finden und sorgt so für eine reiche für die Ernte deiner Pflanzen zu finden und sorgt so für eine reiche
Belohnung. Belohnung.
</p> </p>
</div> </div>
<img <img
className="smallPicture" className="smallPicture"
src="../../public/recources/images/Cucumber.webp" src="../../public/recources/images/Cucumber.webp"
></img> alt="cucumber"></img>
</div> </div>
</main> </main>
); );
} }
function StartNowLink() { function StartNowLink() {
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false); const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
checkJwtStatus(setIsLoggedIn); checkJwtStatus(setIsLoggedIn);
return ( return (
<div className="link"> <div className="link">
<NavLink to={isLoggedIn ? "/suchen" : "/login"}> <NavLink to={isLoggedIn ? "/suchen" : "/login"}>
<h3> Starte hier</h3> <h3>Starte hier</h3>
<p>und füge Pflanzen zu deinem Garten hinzu</p> <p>und füge Pflanzen zu deinem Garten hinzu</p>
</NavLink> </NavLink>
</div> </div>
); );
} }
export default Home; export default Home;
import "font-awesome/css/font-awesome.min.css"; import "font-awesome/css/font-awesome.min.css";
import "../stylesheets/Suche.css"; import "../stylesheets/Suche.css";
import PlantsOverview from "../components/PlantsOverview"; import PlantsOverview from "../components/PlantsOverview";
import {useEffect, useState} from "react"; import { useEffect, useState } from "react";
import FilterPage from "../components/FilterPage"; import FilterPage from "../components/FilterPage";
import {GroundType, LightingDemand, NutrientDemand, Plant, WaterDemand,} from "../utils/schemas"; import {
import {IBackendConnector} from "../utils/IBackendConnector"; GroundType,
LightingDemand,
NutrientDemand,
Plant,
WaterDemand,
} from "../utils/schemas";
import { IBackendConnector } from "../utils/IBackendConnector";
import useGrowbrosFetcher from "../utils/useGrowbrosFetcher"; import useGrowbrosFetcher from "../utils/useGrowbrosFetcher";
type SearchBarProps = { type SearchBarProps = {
setError: (value: any) => void; setError: (value: any) => void;
setPlants: (value: Plant[]) => void; setPlants: (value: Plant[]) => void;
}; };
function Suche() { function Suche() {
const [plants, setPlants] = useState<Plant[]>([]); const [plants, setPlants] = useState<Plant[]>([]);
const [error, setError] = useState({}); const [error, setError] = useState<object>();
const randomPlantsCount: number = 100; const randomPlantsCount: number = 100;
const bc: IBackendConnector = useGrowbrosFetcher(); const bc: IBackendConnector = useGrowbrosFetcher();
useEffect(() => { useEffect(() => {
(async () => { (async () => {
const result = await bc.getRandomPlants(randomPlantsCount); const result = await bc.getRandomPlants(randomPlantsCount);
if (result.err) { if (result.err) {
setError(result.err); setError(result.err);
} else { } else {
setPlants(result.value!); setPlants(result.value!);
} }
})(); })();
}, []); }, []);
return ( return (
<> <>
<SearchBar setError={setError} setPlants={setPlants}/> <SearchBar setError={setError} setPlants={setPlants} />
<div> <div>
{plants.length === 0 && ( {plants.length === 0 && (
<div> <div>
Es wurden keine Pflanzen passend zu deiner Suchanfrage gefunden Es wurden keine Pflanzen passend zu deiner Suchanfrage gefunden
</div> </div>
)} )}
{ {error && <div>Es ist ein Fehler aufgetreten.</div>}
error && ( <PlantsOverview plants={plants} />
<div> </div>
Es ist ein Fehler aufgetreten. </>
</div> );
)
}
<PlantsOverview plants={plants}/>
</div>
</>
);
} }
function SearchBar({setPlants, setError}: SearchBarProps) { function SearchBar({ setPlants, setError }: SearchBarProps) {
const [isComponentVisible, setComponentVisible] = useState(false); const [isComponentVisible, setComponentVisible] = useState(false);
const [searchTerm, setSearchTerm] = useState<string>(""); const [searchTerm, setSearchTerm] = useState<string>("");
const [waterDemand, setWaterDemand] = useState<WaterDemand>(); const [waterDemand, setWaterDemand] = useState<WaterDemand>();
const [nutrientDemand, setNutrientDemand] = useState<NutrientDemand>(); const [nutrientDemand, setNutrientDemand] = useState<NutrientDemand>();
const [lightingDemand, setLightingDemand] = useState<LightingDemand>(); const [lightingDemand, setLightingDemand] = useState<LightingDemand>();
const [groundType, setGroundType] = useState<GroundType>(); const [groundType, setGroundType] = useState<GroundType>();
const [plantDuration, setPlantDuration] = useState<[number, number]>(); const [plantDuration, setPlantDuration] = useState<[number, number]>();
const [growthDuration, setGrowthDuration] = useState<[number, number]>(); const [growthDuration, setGrowthDuration] = useState<[number, number]>();
const bc: IBackendConnector = useGrowbrosFetcher(); const bc: IBackendConnector = useGrowbrosFetcher();
const handleSearchChange = (event: any) => {
setSearchTerm(event.target.value);
};
const loadComponent = () => {
setComponentVisible(!isComponentVisible);
};
const handleSearchRequestSubmit = async () => { const handleSearchChange = (event: any) => {
let result; setSearchTerm(event.target.value);
};
const loadComponent = () => {
setComponentVisible(!isComponentVisible);
};
if (isSearchSetToDefault()) { const handleSearchRequestSubmit = async () => {
result = await bc.getRandomPlants(100); let result;
} else {
result = await bc.searchPlants({
searchTerm,
waterDemand,
nutrientDemand,
lightingDemand,
groundType,
growthDurationMin: growthDuration && growthDuration[0],
growthDurationMax: growthDuration && growthDuration[1],
plantWeekStart: plantDuration && plantDuration[0],
plantWeekEnd: plantDuration && plantDuration[1],
});
}
if (result.err) { if (isSearchSetToDefault()) {
setError(result.err); result = await bc.getRandomPlants(100);
} else { } else {
console.log(result.value); result = await bc.searchPlants({
setPlants(result.value!); searchTerm,
} waterDemand,
}; nutrientDemand,
lightingDemand,
groundType,
growthDurationMin: growthDuration && growthDuration[0],
growthDurationMax: growthDuration && growthDuration[1],
plantWeekStart: plantDuration && plantDuration[0],
plantWeekEnd: plantDuration && plantDuration[1],
});
}
const isSearchSetToDefault = () => { if (result.err) {
return (!searchTerm || searchTerm.trim() === "") && !waterDemand && !lightingDemand && !nutrientDemand && !groundType setError(result.err);
&& (!plantDuration || plantDuration[0] === 1 && plantDuration[1] === 52) } else {
&& (!growthDuration || growthDuration[0] === 1 && growthDuration[1] === 52) console.log(result.value);
setPlants(result.value!);
} }
};
const isSearchSetToDefault = () => {
return ( return (
<> (!searchTerm || searchTerm.trim() === "") &&
<div className="searchBar"> !waterDemand &&
<h2>Suche nach einer Pflanze</h2> !lightingDemand &&
<p> !nutrientDemand &&
und füge diese dann zu deinem Garten oder zu deiner Wunschliste hinzu !groundType &&
</p> (!plantDuration || (plantDuration[0] === 1 && plantDuration[1] === 52)) &&
<div className="searchFilter"> (!growthDuration || (growthDuration[0] === 1 && growthDuration[1] === 52))
<input
onChange={handleSearchChange}
value={searchTerm}
type="text"
placeholder="Pflanze suchen..."
/>
<button onClick={loadComponent} className="filter">
<i className="fa fa-filter"/>
</button>
<button onClick={handleSearchRequestSubmit}>
<i className="fa fa-search"></i>
</button>
</div>
<div
style={
isComponentVisible ? {display: "block"} : {display: "none"}
}
>
<FilterPage
setGroundType={setGroundType}
setGrowthDuration={setGrowthDuration}
setLightingDemand={setLightingDemand}
setNutrientDemand={setNutrientDemand}
setPlantDuration={setPlantDuration}
setWaterDemand={setWaterDemand}
/>
</div>
</div>
</>
); );
};
return (
<>
<div className="searchBar">
<h2>Suche nach einer Pflanze</h2>
<p>
und füge diese dann zu deinem Garten oder zu deiner Wunschliste hinzu
</p>
<div className="searchFilter">
<input
onChange={handleSearchChange}
value={searchTerm}
type="text"
placeholder="Pflanze suchen..."
/>
<button onClick={loadComponent} className="filter">
<i className="fa fa-filter" />
</button>
<button onClick={handleSearchRequestSubmit}>
<i className="fa fa-search"></i>
</button>
</div>
<div
style={
isComponentVisible ? { display: "block" } : { display: "none" }
}
>
<FilterPage
setGroundType={setGroundType}
setGrowthDuration={setGrowthDuration}
setLightingDemand={setLightingDemand}
setNutrientDemand={setNutrientDemand}
setPlantDuration={setPlantDuration}
setWaterDemand={setWaterDemand}
/>
</div>
</div>
</>
);
} }
export default Suche; export default Suche;
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
} }
.descriptionAside p { .descriptionAside p {
font-size: 1.25rem;
padding-top: 20px; padding-top: 20px;
font-family: Century Gothic, sans-serif; font-family: Century Gothic, sans-serif;
} }
...@@ -37,6 +38,8 @@ ...@@ -37,6 +38,8 @@
font-size: 1.5rem; font-size: 1.5rem;
font-weight: 500; font-weight: 500;
font-family: Century Gothic, sans-serif; font-family: Century Gothic, sans-serif;
text-align: center;
text-wrap: pretty; /*ja, das funktioniert, auch wenns rot unterstrichen ist (keine einzelnen wörter in der letzten zeile)*/
} }
.smallDiv { .smallDiv {
...@@ -57,20 +60,18 @@ ...@@ -57,20 +60,18 @@
.link a { .link a {
color: #4d5927; color: #4d5927;
font-size: 1.3rem;
} }
.link p { .link p {
font-size: 1rem;
padding-top: 5px; padding-top: 5px;
} }
.link{ .link {
margin-top: 30px; margin-top: 30px;
transition: transform 0.25s ease-in-out; font-size: 1.25rem;
transition: transform 0.15s ease-in-out;
} }
.link:hover{ .link:hover {
transform: scale(1.025); transform: scale(1.025);
} }
\ No newline at end of file
package hdm.mi.growbros.config; package hdm.mi.growbros.config;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@Configuration @Configuration
@EnableJpaAuditing @EnableJpaAuditing
@Profile("!test")
public class JpaConfiguration { public class JpaConfiguration {
} }
package hdm.mi.growbros.config;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@TestConfiguration
@EnableJpaAuditing(setDates = false)
public class TestJpaAuditing {
}
package hdm.mi.growbros.controllers;
import hdm.mi.growbros.models.plant.Plant;
import hdm.mi.growbros.service.WishListService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class WishListControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private WishListService wishlistService;
@Test
@WithMockUser
void wishlistEntries_shouldBeOkJson_containList() throws Exception {
when(wishlistService.getWishedPlants(any(), any())).thenReturn(List.of(Plant.builder().name("Plant").build()));
mvc.perform(get("/api/v1/wishlist").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.size()").value(1))
.andDo(print());
verify(wishlistService).getWishedPlants(any(), any());
}
@Test
@WithMockUser
void creatingEntry_shouldBe201_andContainPlant() throws Exception {
mvc.perform(post("/api/v1/wishlist/add/0").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated())
.andDo(print());
verify(wishlistService).addPlantToWishList(anyLong(), any());
}
@Test
@WithMockUser
void deletingEntry_shouldBe204() throws Exception {
mvc.perform(delete("/api/v1/wishlist/remove/1").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent())
.andDo(print());
}
}
\ No newline at end of file
...@@ -33,8 +33,12 @@ public class GardenIntegrationTest extends BaseIntegrationTest { ...@@ -33,8 +33,12 @@ public class GardenIntegrationTest extends BaseIntegrationTest {
@Test @Test
void shouldAddPlant_code201() throws Exception { void shouldAddPlant_code201() throws Exception {
mvc.perform(post(gardenUrl + "/add/1").headers(authHeader)) mvc.perform(post(gardenUrl + "/add/1").headers(authHeader))
.andExpect(status().isCreated()) .andExpect(status().isCreated());
.andExpect(jsonPath("$.name").value(pflanzeNumeroUno.getName()));
mvc.perform(get(gardenUrl).headers(authHeader))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()").value(1))
.andExpect(jsonPath("$[0].name").value(pflanzeNumeroUno.getName()));
} }
@Test @Test
...@@ -72,5 +76,9 @@ public class GardenIntegrationTest extends BaseIntegrationTest { ...@@ -72,5 +76,9 @@ public class GardenIntegrationTest extends BaseIntegrationTest {
mvc.perform(delete(gardenUrl + "/remove/all").headers(authHeader)) mvc.perform(delete(gardenUrl + "/remove/all").headers(authHeader))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string("2")); .andExpect(content().string("2"));
mvc.perform(get(gardenUrl + "/count").headers(authHeader))
.andExpect(status().isOk())
.andExpect(content().string("0"));
} }
} }
package hdm.mi.growbros.repositories;
import hdm.mi.growbros.models.WishListEntry;
import hdm.mi.growbros.models.plant.Plant;
import hdm.mi.growbros.models.user.Role;
import hdm.mi.growbros.models.user.User;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.ActiveProfiles;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DataJpaTest
@ActiveProfiles("test")
public class WishListRepositoryTest {
@Autowired
private WishListRepository underTest;
@Autowired
private TestEntityManager entityManager;
private WishListEntry we1, we2, we3, we4;
private User user1, user2;
@BeforeEach
void setupEach() {
user1 = User.builder()
.email("user1@gmail.com")
.role(Role.USER)
.password("safePassword123!")
.build();
entityManager.persist(user1);
user2 = User.builder()
.email("user2@gmail.com")
.role(Role.USER)
.password("safePassword123!")
.build();
entityManager.persist(user2);
Plant p1 = Plant.builder()
.name("Early plant")
.plantWeekStart(1)
.plantWeekEnd(3)
.harvestWeekStart(10)
.harvestWeekEnd(15)
.build();
entityManager.persist(p1);
Plant p2 = Plant.builder()
.name("Medium late plant")
.plantWeekStart(4)
.plantWeekEnd(6)
.harvestWeekStart(18)
.harvestWeekEnd(20)
.build();
entityManager.persist(p2);
Plant p3 = Plant.builder()
.name("Late plant")
.plantWeekStart(10)
.plantWeekEnd(15)
.harvestWeekStart(25)
.harvestWeekEnd(30)
.build();
entityManager.persist(p3);
we1 = WishListEntry.builder().id(0L).plant(p1).user(user1).build();
we2 = WishListEntry.builder().id(0L).plant(p2).user(user1).build();
we3 = WishListEntry.builder().id(0L).plant(p3).user(user1).build();
we4 = WishListEntry.builder().id(0L).plant(p3).user(user2).build();
underTest.saveAll(List.of(we1, we2, we3, we4));
}
@Test
void shouldReturnCorrectList_ofEntriesForUser_orderedByNearestPlantingWeek() {
//when
var result = underTest.findAllByNearestPlantingWeek(user1, 1);
//expect
assertEquals(List.of(we1, we2, we3), result);
}
@Test
void shouldReturnCorrectList_ofEntriesForUser_orderedByCurrentPlantingWeek() {
//when
var result = underTest.findByCurrentPlantingWeek(user1, 1);
//expect
assertEquals(List.of(we1), result);
}
@Test
void shouldDeleteCorrectEntries_whenClearingGarden_forUser() {
//when
int deleted = underTest.deleteAllByUser(user2);
//expect
assertEquals(1, deleted);
assertEquals(List.of(), underTest.findByUser(user2));
assertThat(List.of(we1, we2, we3)).hasSameElementsAs(underTest.findByUser(user1));
}
@Test
void countShouldBeCorrect_forUser() {
//when
int user1Count = underTest.countByUser(user1);
int user2Count = underTest.countByUser(user2);
//expect
assertEquals(3, user1Count);
assertEquals(1, user2Count);
}
}
package hdm.mi.growbros.service;
import hdm.mi.growbros.config.UserTestData;
import hdm.mi.growbros.exceptions.PlantNotFoundException;
import hdm.mi.growbros.models.WishListEntry;
import hdm.mi.growbros.models.plant.Plant;
import hdm.mi.growbros.models.user.Role;
import hdm.mi.growbros.models.user.User;
import hdm.mi.growbros.repositories.PlantRepository;
import hdm.mi.growbros.repositories.WishListRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ActiveProfiles;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@SpringBootTest
@ActiveProfiles("test")
public class WishListServiceTest {
@MockBean
private WishListRepository wishListRepository;
@MockBean
private PlantRepository plantRepository;
@Autowired
private WishListService wishListService;
@MockBean
private TestEntityManager entityManager;
private User user1;
@BeforeEach
void setupEach() {
user1 = User.builder()
.email("user1@gmail.com")
.role(Role.USER)
.password("safePassword123!")
.build();
entityManager.persist(user1);
}
@Test
void shouldThrow_whenAddingPlant_thatDoesNotExist() {
//setup
when(plantRepository.findById(100L)).thenReturn(Optional.empty());
assertThrows(PlantNotFoundException.class, () -> wishListService.addPlantToWishList(100L, UserTestData.getUser1()));
}
@Test
void shouldReturn_whenAddingExistingPlant() {
//setup
Plant plant = Plant.builder().name("Plant!").build();
when(plantRepository.findById(1L)).thenReturn(Optional.of(plant));
when(wishListRepository.countByUser(user1)).thenReturn(1);
//assert
wishListService.addPlantToWishList(1L, user1);
assertEquals(1, wishListService.countByUser(user1));
}
@Test
void shouldReturnEntries_inCorrectOrder() {
//setup
Plant p1 = Plant.builder().name("AAA").build();
Plant p2 = Plant.builder().name("BBB").build();
Plant p3 = Plant.builder().name("CCC").build();
var orderedByNameMock = List.of(p1, p2, p3);
var orderedByCreatedMock = List.of(p2, p1, p3);
var orderedByPlantDateMock = List.of(p3, p2, p1);
var orderedByCurrentPlantDateMock = List.of(p2);
User user = UserTestData.getUser1();
when(wishListRepository.findByUser(user, Sort.by("createdAt").descending())).thenReturn(mapPlantsToWishListEntries(orderedByCreatedMock, user));
when(wishListRepository.findByUser(user, Sort.by("plant.name"))).thenReturn(mapPlantsToWishListEntries(orderedByNameMock, user));
when(wishListRepository.findAllByNearestPlantingWeek(eq(user), any(int.class))).thenReturn(mapPlantsToWishListEntries(orderedByPlantDateMock, user));
when(wishListRepository.findByCurrentPlantingWeek(eq(user), any(int.class))).thenReturn(mapPlantsToWishListEntries(orderedByCurrentPlantDateMock, user));
assertAll(
() -> assertEquals(orderedByNameMock, wishListService.getWishedPlants(user, null)),
() -> assertEquals(orderedByNameMock, wishListService.getWishedPlants(user, "random stuff")),
() -> assertEquals(orderedByCreatedMock, wishListService.getWishedPlants(user, "createdAt")),
() -> assertEquals(orderedByPlantDateMock, wishListService.getWishedPlants(user, "plantDate")),
() -> assertEquals(orderedByCurrentPlantDateMock, wishListService.getWishedPlants(user, "currentPlantable"))
);
}
private List<WishListEntry> mapPlantsToWishListEntries(List<Plant> plants, User user) {
return plants.stream()
.map(plant -> WishListEntry.builder().plant(plant).id(0L).user(user).build())
.toList();
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment