Merge pull request 'chore: Update branch' (#10) from Maradona/Backend:restfull into main

Reviewed-on: 3001291/Backend#10
pull/1/head
Tarik Gökmen 2024-11-09 14:14:47 +01:00
commit cf608ceb28
33 changed files with 492 additions and 108 deletions

8
.gitignore vendored
View File

@ -31,3 +31,11 @@ build/
### VS Code ###
.vscode/
.history/
### macOS ###
*.DS_Store
### Database files ###
*.db
*.mv.db

Binary file not shown.

View File

@ -102,6 +102,7 @@
<destDir>${project.build.directory}/docs</destDir>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -33,13 +33,13 @@ public class DefaultValueLoader implements CommandLineRunner {
private FormOfAddressRepository formOfAddressRepository;
@Override
public void run(String... args) throws Exception {
public void run(String... args) {
// Clear all tables
// formOfAddressRepository.deleteAll();
// employeeRepository.deleteAll();
// secondarySkillRepository.deleteAll();
// primarySkillRepository.deleteAll();
employeeRepository.deleteAll();
secondarySkillRepository.deleteAll();
primarySkillRepository.deleteAll();
formOfAddressRepository.deleteAll();
// Create form of addresses
FormOfAddress formOfAddress1 = new FormOfAddress();

View File

@ -0,0 +1,113 @@
package com.maradona.backend.controllers.api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.maradona.backend.entities.FormOfAddress;
import com.maradona.backend.services.FormOfAddressService;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.PutMapping;
/**
* Controller for routing to the form of address related API endpoints.
*
* List of endpoints:
* - GET /api/form-of-adress
* - GET /api/form-of-adress/all
* - POST /api/form-of-adress
* - PUT /api/form-of-adress
* - DELETE /api/form-of-adress
*
* @see com.maradona.backend.entities.FormOfAddress
*/
@RestController
@RequestMapping("/api/form-of-adress")
class FormOfAdressController {
@Autowired
private FormOfAddressService formOfAdressService;
/**
* Returns the form of address with the given ID.
*
* @param id The ID of the form of address to return.
* @return The description of the form of address with the given ID.
* @see com.maradona.backend.entities.FormOfAddress
*/
@GetMapping({ "/", "" })
public ResponseEntity<FormOfAddress> getFormOfAdress(@RequestParam Long id) {
return formOfAdressService.getFormOfAddressById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* Returns all form of addresses.
*
* @return A list of all form of addresses.
* @see com.maradona.backend.entities.FormOfAddress
*/
@GetMapping("/all")
public ResponseEntity<Iterable<FormOfAddress>> getAllFormOfAdresses() {
Iterable<FormOfAddress> formOfAddresses = formOfAdressService.getAllFormOfAddresses();
return ResponseEntity.ok(formOfAddresses);
}
/**
* Creates a new form of address.
*
* @param description The description of the new form of address.
* @return The ID of the newly created form of address.
* @see com.maradona.backend.entities.FormOfAddress
*/
@PostMapping({ "/", "" })
public ResponseEntity<Void> createFormOfAdress(@RequestBody String description) {
var formOfAddress = new FormOfAddress();
formOfAddress.setDescription(description);
formOfAdressService.saveFormOfAddress(formOfAddress);
return ResponseEntity.ok().build();
}
/**
* Updates the description of a form of address.
*
* @param id The ID of the form of address to update.
* @param description The new description of the form of address.
* @return The updated form of address.
* @see com.maradona.backend.entities.FormOfAddress
*/
@PutMapping({ "/", "" })
public ResponseEntity<Void> updateFormOfAdress(@RequestParam Long id, @RequestBody String description) {
var formOfAddress = formOfAdressService.getFormOfAddressById(id).orElse(null);
if (formOfAddress == null) {
return ResponseEntity.notFound().build();
}
formOfAddress.setDescription(description);
formOfAdressService.saveFormOfAddress(formOfAddress);
return ResponseEntity.ok().build();
}
/**
* Deletes a form of address.
*
* @param id The ID of the form of address to delete.
* @return The deleted form of address.
* @see com.maradona.backend.entities.FormOfAddress
*/
@DeleteMapping({ "/", "" })
public ResponseEntity<Void> deleteFormOfAdress(@RequestParam Long id) {
var formOfAddress = formOfAdressService.getFormOfAddressById(id).orElse(null);
if (formOfAddress == null) {
return ResponseEntity.notFound().build();
}
formOfAdressService.deleteFormOfAddress(id);
return ResponseEntity.ok().build();
}
}

View File

@ -28,7 +28,6 @@ import com.maradona.backend.services.ProjectService;
* - PUT /api/project
* - DELETE /api/project
*
* @warning The ProjectService is not accounting for the user yet.
* @see com.maradona.backend.entities.Project
* @see com.maradona.backend.services.ProjectService
*/

View File

@ -9,21 +9,21 @@ public class Page {
@GetMapping({ "/", "" })
public String home(Model model) {
return "redirect:/projects";
return "pages/core/home";
}
@GetMapping("/impressum")
public String impressum() {
return "core/impressum";
return "pages/core/impressum";
}
@GetMapping("/datenschutz")
public String datenschutz() {
return "core/datenschutz";
return "pages/core/datenschutz";
}
@GetMapping("/notes")
public String notes() {
return "notes/notes";
return "pages/core/notes";
}
}

View File

@ -15,7 +15,7 @@ import com.maradona.backend.services.ProjectService;
* - GET /projects
* - GET /projects/create
*
* @see Project
* @see com.maradona.backend.entities.Project
*/
@Controller
@RequestMapping("/projects")
@ -32,13 +32,13 @@ public class ProjectPage {
*
* @param model The model with the data to be displayed.
* @return The projects overview page template
* @see Project
* @see com.maradona.backend.entities.Project
*/
@GetMapping({ "/", "" })
public String projects(Model model) {
var projects = projectService.getAllProjects();
model.addAttribute("projects", projects);
return "projects/projects";
return "/pages/projects/overview";
}
/**
@ -46,11 +46,11 @@ public class ProjectPage {
*
* @param model The model with the data to be displayed.
* @return The project creation page template
* @see Project
* @see com.maradona.backend.entities.Project
*/
@GetMapping("/create")
public String create(Model model) {
model.addAttribute("project", new Project());
return "projects/projects-create";
return "/pages/projects/create";
}
}

View File

@ -7,8 +7,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import com.maradona.backend.entities.SecondarySkill;
import com.maradona.backend.entities.Employee;
import com.maradona.backend.entities.PrimarySkill;
import com.maradona.backend.services.EmployeeService;
import com.maradona.backend.services.SkillService;
import com.maradona.backend.services.PrimarySkillService;
@ -22,6 +20,7 @@ import com.maradona.backend.dto.SkillPrototype;
* - GET /skills/create
* - GET /skills/add
*
* @see com.maradona.backend.entities.SecondarySkill
*/
@Controller
@RequestMapping("/skills")
@ -47,15 +46,15 @@ public class SkillsPage {
* "employee" and a SkillsDto object "skills".
*
* @param model The model with the data to be displayed.
* @return The skills overview page template
* @see Employee
* @see SkillsDto
* @return The skills overview page template.
* @see com.maradona.backend.entities.Employee
* @see com.maradona.backend.dto.SkillsDto
*/
@GetMapping({ "/", "" })
public String profile(Model model) {
model.addAttribute("employee", employeeService.getEmployeeById(user).orElse(null));
model.addAttribute("skills", skillService.getUserSkills(user));
return "skills/skills";
return "/pages/skills/overview";
}
/**
@ -66,12 +65,12 @@ public class SkillsPage {
*
* @param model The model with the data to be displayed
* @return The create skill page template
* @see SecondarySkill
* @see com.maradona.backend.entities.SecondarySkill
*/
@GetMapping("/create")
public String createSkill(Model model) {
model.addAttribute("secondarySkill", new SecondarySkill());
return "skills/skills-create";
return "/pages/skills/create";
}
/**
@ -87,16 +86,16 @@ public class SkillsPage {
*
* @param model The model with the data to be displayed
* @return The add skill page template
* @see PrimarySkill
* @see SecondarySkill
* @see SkillPrototype
* @see com.maradona.backend.entities.PrimarySkill
* @see com.maradona.backend.entities.SecondarySkill
* @see com.maradona.backend.dto.SkillPrototype
*/
@GetMapping("/add")
public String addSkill(Model model) {
// TODO: Make sure it returns the correct initail data for secondary skills
model.addAttribute("primarySkills", primarySkillService.getAllPrimarySkills());
model.addAttribute("skillProtoype", new SkillPrototype());
return "skills/skills-add";
return "/pages/skills/add";
}
}

View File

@ -2,6 +2,7 @@ package com.maradona.backend.entities;
import jakarta.persistence.*;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import java.time.LocalTime;
import java.util.List;
import java.util.Map;
@ -10,7 +11,6 @@ import java.util.stream.Collectors;
@Entity
@Data
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ -31,6 +31,7 @@ public class Employee {
private LocalTime dEnd;
@OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference
private List<EmployeeSecondarySkill> secondarySkills;
public void setEmployeeNr(Integer employeeNr) {

View File

@ -1,13 +1,12 @@
package com.maradona.backend.entities;
import jakarta.persistence.*;
import com.fasterxml.jackson.annotation.JsonBackReference;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonBackReference;
@Entity
@Data
public class EmployeeSecondarySkill {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ -19,7 +18,6 @@ public class EmployeeSecondarySkill {
@ManyToOne
@JoinColumn(name = "secondary_skill_id", nullable = false)
@JsonBackReference
private SecondarySkill secondarySkill;
@Column(nullable = false)

View File

@ -28,4 +28,14 @@ public class FormOfAddressService {
public Iterable<FormOfAddress> getAllFormOfAddresses() {
return formOfAddressRepository.findAll();
}
public Long updateFormOfAddress(Long id, String description) {
var formOfAddress = formOfAddressRepository.findById(id).orElse(null);
if (formOfAddress != null) {
formOfAddress.setDescription(description);
formOfAddressRepository.save(formOfAddress);
return id;
}
return null;
}
}

View File

@ -0,0 +1,18 @@
document.getElementById("primarySkill").addEventListener("change", function () {
var primarySkillId = this.value;
fetch("/skills/secondary-skills?primarySkillId=" + primarySkillId)
.then((response) => response.json())
.then((data) => {
console.log("Fetch response data:", data);
var secondarySkillSelect = document.getElementById("secondarySkill");
secondarySkillSelect.innerHTML =
'<option value="" disabled selected>Select a secondary skill</option>';
data.forEach(function (secondarySkill) {
var option = document.createElement("option");
option.value = secondarySkill.ssid;
option.text = secondarySkill.description;
secondarySkillSelect.add(option);
});
})
.catch((error) => console.error("Error fetching secondary skills:", error));
});

View File

@ -1 +0,0 @@
/* Placeholder */

View File

@ -5,26 +5,9 @@
<img src="/assets/inter-brand.svg" alt="INTER Logo" class="svg-logo" />
</div>
<div class="col-md-6">
<nav>
<ul class="nav justify-content-end">
<li class="nav-item">
<a
class="nav-link"
th:href="@{/projects}"
th:classappend="${activePage == 'projects' ? 'active' : ''}"
>Projekte</a
>
</li>
<li class="nav-item">
<a
class="nav-link"
th:href="@{/skills}"
th:classappend="${activePage == 'profile' ? 'active' : ''}"
>Skills</a
>
</li>
</ul>
</nav>
<div
th:replace="~{/core/_navigation :: navigation(activePage=${activePage})}"
></div>
</div>
</div>
</div>

View File

@ -0,0 +1,22 @@
<div th:fragment="navigation">
<nav>
<ul class="nav justify-content-end">
<li class="nav-item">
<a
class="nav-link"
th:href="@{/projects}"
th:classappend="${activePage == 'projects' ? 'active' : ''}"
>Projekte</a
>
</li>
<li class="nav-item">
<a
class="nav-link"
th:href="@{/skills}"
th:classappend="${activePage == 'profile' ? 'active' : ''}"
>Skills</a
>
</li>
</ul>
</nav>
</div>

View File

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>My Skill Management System</title>
<link rel="stylesheet" href="style/notes/notes.css" />
</head>
<body>
<div class="wrapper">
<div th:replace="~{/core/_header :: header(activePage=${notes})}"></div>
<div class="content container mt-4"></div>
<div th:replace="~{/core/_footer :: footer}"></div>
</div>
</body>
</html>

View File

@ -3,10 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Create Project</title>
<link rel="stylesheet" href="/style/projects/projects.css" />
<link rel="stylesheet" href="/style/core/style.css" />
<link rel="stylesheet" href="/style/core/header.css" />
<link rel="stylesheet" href="/style/core/footer.css" />
<link rel="stylesheet" href="/css/projects/projects.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">

View File

@ -3,7 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Projects</title>
<link rel="stylesheet" href="/style/projects/projects.css" />
<link rel="stylesheet" href="/css/projects/projects.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">

View File

@ -3,10 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Add Skill</title>
<link rel="stylesheet" href="/style/skills/skills.css" />
<link rel="stylesheet" href="/style/core/style.css" />
<link rel="stylesheet" href="/style/core/header.css" />
<link rel="stylesheet" href="/style/core/footer.css" />
<link rel="stylesheet" href="/css/skills/skills.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">
@ -15,11 +15,12 @@
<h2 class="mb-4">Add Skill</h2>
<form
th:action="@{/skills/add}"
th:object="${skillForm}"
th:object="${skillProtoype}"
method="post"
class="project-card"
>
<div class="card-body">
<!-- Primary Skill Dropdown -->
<div class="form-group">
<label for="primarySkill">Primary Skill</label>
<select
@ -28,6 +29,9 @@
class="form-control"
th:field="*{primarySkillId}"
>
<option value="" disabled selected>
Select a primary skill
</option>
<option
th:each="primarySkill : ${primarySkills}"
th:value="${primarySkill.psid}"
@ -35,6 +39,8 @@
></option>
</select>
</div>
<!-- Secondary Skill Dropdown -->
<div class="form-group">
<label for="secondarySkill">Secondary Skill</label>
<select
@ -48,6 +54,8 @@
</option>
</select>
</div>
<!-- Skill Level Dropdown -->
<div class="form-group">
<label for="level">Level</label>
<select
@ -56,6 +64,7 @@
class="form-control"
th:field="*{level}"
>
<option value="" disabled selected>Select skill level</option>
<option
th:each="level : ${#numbers.sequence(1, 5)}"
th:value="${level}"
@ -63,36 +72,14 @@
></option>
</select>
</div>
<!-- Submit Button -->
<button type="submit" class="btn-create-project">Add Skill</button>
</div>
</form>
</div>
<div th:replace="~{/core/_footer :: footer}"></div>
</div>
<script>
document
.getElementById("primarySkill")
.addEventListener("change", function () {
var primarySkillId = this.value;
fetch("/skills/secondary-skills?primarySkillId=" + primarySkillId)
.then((response) => response.json())
.then((data) => {
console.log("Fetch response data:", data);
var secondarySkillSelect =
document.getElementById("secondarySkill");
secondarySkillSelect.innerHTML =
'<option value="" disabled selected>Select a secondary skill</option>';
data.forEach(function (secondarySkill) {
var option = document.createElement("option");
option.value = secondarySkill.ssid;
option.text = secondarySkill.description;
secondarySkillSelect.add(option);
});
})
.catch((error) =>
console.error("Error fetching secondary skills:", error)
);
});
</script>
<script src="/js/skills/fetchSecondarySkills.js"></script>
</body>
</html>

View File

@ -3,10 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Create Skill</title>
<link rel="stylesheet" href="/style/skills/skills.css" />
<link rel="stylesheet" href="/style/core/style.css" />
<link rel="stylesheet" href="/style/core/header.css" />
<link rel="stylesheet" href="/style/core/footer.css" />
<link rel="stylesheet" href="/css/skills/skills.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">

View File

@ -3,8 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Skills</title>
<link rel="stylesheet" href="/style/skills/skills.css" />
<link rel="stylesheet" href="/style/core/style.css" />
<link rel="stylesheet" href="/css/skills/skills.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">

View File

@ -0,0 +1,57 @@
package com.maradona.backend.controllers.api;
import com.maradona.backend.controllers.api.EmployeeController;
import com.maradona.backend.entities.Employee;
import com.maradona.backend.services.EmployeeService;
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.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalTime;
import java.util.Optional;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@WebMvcTest(EmployeeController.class)
public class EmployeeControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private EmployeeService employeeService;
@Test
public void testGetEmployeeById() throws Exception {
// Arrange: Mock an employee
Employee employee = new Employee();
employee.setId(1L);
employee.setEmployeeNr(123);
employee.setFirstName("John");
employee.setLastName("Doe");
employee.setDStart(LocalTime.of(9, 0));
employee.setDEnd(LocalTime.of(17, 0));
// Assuming FormOfAddress and EmployeeSecondarySkill are also set up if needed for your test.
when(employeeService.getEmployeeById(1L)).thenReturn(Optional.of(employee));
// Act & Assert: Send GET request and expect a 200 OK status and JSON response
mockMvc.perform(get("/api/employee")
.param("id", "1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.employeeNr").value(123))
.andExpect(jsonPath("$.firstName").value("John"))
.andExpect(jsonPath("$.lastName").value("Doe"))
.andExpect(jsonPath("$.dStart").value("09:00:00"))
.andExpect(jsonPath("$.dEnd").value("17:00:00"));
}
}

View File

@ -0,0 +1,199 @@
package com.maradona.backend.controllers.api;
import com.maradona.backend.controllers.api.ProjectController;
import com.maradona.backend.entities.Project;
import com.maradona.backend.services.ProjectService;
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.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@WebMvcTest(ProjectController.class)
public class ProjectControllerTest {
// MockMvc to simulate HTTP requests to the controller
@Autowired
private MockMvc mockMvc;
// Mocked ProjectService to simulate service calls
@MockBean
private ProjectService projectService;
@Test
public void testGetProjectById() throws Exception {
//Arrange: Mock an project
Project project = new Project();
project.setId(1L);
project.setName("Skillmanagementsystem erstellen");
project.setStartDate(LocalDate.of(2024, 11,8));
project.setEndDate(LocalDate.of(2024, 11,20));
project.setWorkload(12);
project.setDescription("Skillmanagementsystem erstellen für die Firma");
//Define the behavior of the mocked ProjectService: return the project when ID 1 is requested
when(projectService.getProjectById(1L)).thenReturn(Optional.of(project));
//Act & Assert: Send GET request an expect a 200 OK status and JSON response
//GET /api/project/
mockMvc.perform(get("/api/project")
.param("id", "1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Skillmanagementsystem erstellen"))
.andExpect(jsonPath("$.startDate").value("2024-11-08"))
.andExpect(jsonPath("$.endDate").value("2024-11-20"))
.andExpect(jsonPath("$.workload").value(12))
.andExpect(jsonPath("$.description").value("Skillmanagementsystem erstellen für die Firma"));
}
@Test
public void testGetAllProjects() throws Exception {
//Arrange: Creat a list of mock projects
Project project = new Project();
project.setId(1L);
project.setName("Skillmanagementsystem erstellen");
project.setStartDate(LocalDate.of(2024, 11,8));
project.setEndDate(LocalDate.of(2024, 11,20));
project.setWorkload(12);
project.setDescription("Skillmanagementsystem erstellen für die Firma");
Project project2 = new Project();
project2.setId(2L);
project2.setName("EAFC 25");
project2.setStartDate(LocalDate.of(2024, 11,20));
project2.setEndDate(LocalDate.of(2024, 11,30));
project2.setWorkload(2);
project2.setDescription("Entwicklung von EAFC 25 für neues Spaß erlebnis");
//Define the behavior of the mocked ProjectService: return a list of projects when requested
when(projectService.getAllProjects()).thenReturn(Arrays.asList(project, project2));
//Act & Assert: Send GET request an expect a 200 Ok status and JSON response with the list of projects
mockMvc.perform(get("/api/project/all")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].id").value(1))
.andExpect(jsonPath("$[0].name").value("Skillmanagementsystem erstellen"))
.andExpect(jsonPath("$[0].startDate").value("2024-11-08"))
.andExpect(jsonPath("$[0].endDate").value("2024-11-20"))
.andExpect(jsonPath("$[0].workload").value(12))
.andExpect(jsonPath("$[0].description").value("Skillmanagementsystem erstellen für die Firma"))
.andExpect(jsonPath("$[1].id").value(2))
.andExpect(jsonPath("$[1].name").value("EAFC 25"))
.andExpect(jsonPath("$[1].startDate").value("2024-11-20"))
.andExpect(jsonPath("$[1].endDate").value("2024-11-30"))
.andExpect(jsonPath("$[1].workload").value(2))
.andExpect(jsonPath("$[1].description").value("Entwicklung von EAFC 25 für neues Spaß erlebnis"));
}
@Test
public void testGetProjectsByUserId() throws Exception {
// Arrange: Mock projects for a specific user
Project project1 = new Project();
project1.setId(1L);
project1.setName("Skill Management System");
project1.setStartDate(LocalDate.of(2024, 11, 8));
project1.setEndDate(LocalDate.of(2024, 11, 20));
project1.setWorkload(12);
project1.setDescription("Create a skill management system for the company");
Project project2 = new Project();
project2.setId(2L);
project2.setName("Project Management Tool");
project2.setStartDate(LocalDate.of(2024, 12, 1));
project2.setEndDate(LocalDate.of(2024, 12, 15));
project2.setWorkload(10);
project2.setDescription("Develop a project management tool");
Long userId = 123L;
// Mock the ProjectService to return projects for a specific user
when(projectService.getProjectsByUserId(userId)).thenReturn(Arrays.asList(project1, project2));
// Act & Assert: Send GET request and expect a 200 OK status with the correct JSON response
mockMvc.perform(get("/api/project/from-user")
.param("userId", String.valueOf(userId))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].id").value(1))
.andExpect(jsonPath("$[0].name").value("Skill Management System"))
.andExpect(jsonPath("$[1].id").value(2))
.andExpect(jsonPath("$[1].name").value("Project Management Tool"));
}
@Test
public void testCreateProject() throws Exception {
// Arrange: Create a new project and set all required fields
Project savedProject = new Project();
savedProject.setId(1L); // ID setzen
savedProject.setName("New Project");
savedProject.setStartDate(LocalDate.of(2024, 11, 10));
savedProject.setEndDate(LocalDate.of(2024, 12, 10));
savedProject.setWorkload(15);
savedProject.setDescription("A new project for testing");
// Mocking the saveProject method to return this specific project
when(projectService.saveProject(any(Project.class))).thenReturn(savedProject);
// Act & Assert: Send POST request and check for 201 Created with JSON response
mockMvc.perform(post("/api/project")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"New Project\",\"startDate\":\"2024-11-10\",\"endDate\":\"2024-12-10\",\"workload\":15,\"description\":\"A new project for testing\"}"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1)) // Ensure the response JSON contains id = 1
.andExpect(jsonPath("$.name").value("New Project"))
.andExpect(jsonPath("$.workload").value(15))
.andExpect(jsonPath("$.description").value("A new project for testing"));
}
@Test
public void testUpdateProject() throws Exception {
// Arrange: Create an existing project
Project project = new Project();
project.setId(1L);
project.setName("Updated Project");
project.setStartDate(LocalDate.of(2024, 11, 10));
project.setEndDate(LocalDate.of(2024, 12, 10));
project.setWorkload(20);
project.setDescription("An updated project description");
// Mock the ProjectService to return the updated project
when(projectService.saveProject(project)).thenReturn(project);
// Act & Assert: Send PUT request and expect a 200 OK status with the updated project as JSON response
mockMvc.perform(put("/api/project")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"id\":1,\"name\":\"Updated Project\",\"startDate\":\"2024-11-10\",\"endDate\":\"2024-12-10\",\"workload\":20,\"description\":\"An updated project description\"}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Updated Project"))
.andExpect(jsonPath("$.workload").value(20))
.andExpect(jsonPath("$.description").value("An updated project description"));
}
@Test
public void testDeleteProject() throws Exception {
// Arrange: Define the ID of the project to delete
Long projectId = 1L;
// No need to mock the delete method as it returns void, we just ensure no exception is thrown
// Act & Assert: Send DELETE request and expect a 204 No Content status
mockMvc.perform(delete("/api/project")
.param("id", String.valueOf(projectId))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent());
}
}