diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/config/SecurityConfig.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/config/SecurityConfig.java new file mode 100644 index 0000000..b15fb10 --- /dev/null +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/config/SecurityConfig.java @@ -0,0 +1,42 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.config; + +import fr.vincent.ramiere.mangerautourdesimplonback.service.JpaUserDetailsService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity +public class SecurityConfig { + + private final JpaUserDetailsService jpaUserDetailsService; + + public SecurityConfig(JpaUserDetailsService jpaUserDetailsService) { + this.jpaUserDetailsService = jpaUserDetailsService; + } + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + return http + .csrf(csrf -> csrf.disable()) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/v1/restaurants/**").permitAll() + .requestMatchers("/api/v1/personnes/**").permitAll() + .anyRequest().authenticated()) + .userDetailsService(jpaUserDetailsService) + .httpBasic(Customizer.withDefaults()) + .build(); + } + + @Bean + PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/PreferenceController.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/PreferenceController.java new file mode 100644 index 0000000..b9ddc17 --- /dev/null +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/PreferenceController.java @@ -0,0 +1,64 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.controller; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Preference; +import fr.vincent.ramiere.mangerautourdesimplonback.service.PreferenceService; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * Controleur REST pour la gestion des préférences des utilisateurs. + */ +@RestController +@RequestMapping("/api/v1/preferences") +@AllArgsConstructor +public class PreferenceController { + + private final PreferenceService preferenceService; + + /** + * DTO pour recevoir les données de préférence. + */ + @Data + static class PreferenceRequest { + private Integer personneId; + private Integer restaurantId; + private Integer note; + private Boolean favori; + } + + /** + * Endpoint pour récupérer toutes les préférences d'un utilisateur. + * + * @param personneId L'ID de l'utilisateur. + * @return une ResponseEntity contenant la liste des préférences et le statut + * HTTP OK. + */ + @GetMapping("/user/{personneId}") + public ResponseEntity> getPreferencesByPersonneId(@PathVariable Integer personneId) { + List preferences = preferenceService.getPreferencesByPersonneId(personneId); + return ResponseEntity.ok(preferences); + } + + /** + * Endpoint pour créer ou mettre à jour une préférence (note et/ou favori). + * + * @param request Le corps de la requête contenant les détails de la préférence. + * @return une ResponseEntity contenant la préférence sauvegardée (OK) ou + * NOT_FOUND si l'utilisateur ou le restaurant n'existe pas. + */ + @PutMapping + public ResponseEntity saveOrUpdatePreference(@RequestBody PreferenceRequest request) { + return preferenceService.saveOrUpdatePreference( + request.getPersonneId(), + request.getRestaurantId(), + request.getNote(), + request.getFavori()) + .map(ResponseEntity::ok) + .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } +} diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/RestaurantController.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/RestaurantController.java new file mode 100644 index 0000000..2ec6436 --- /dev/null +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/RestaurantController.java @@ -0,0 +1,91 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.controller; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Restaurant; +import fr.vincent.ramiere.mangerautourdesimplonback.service.RestaurantService; + +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import lombok.AllArgsConstructor; + +/** + * Controleur REST pour la gestion des restaurants. + * Expose les endpoints de l'API pour les opérations CRUD. + */ +@RestController +@RequestMapping("/api/v1/restaurants") +@AllArgsConstructor +public class RestaurantController { + private final RestaurantService restaurantService; + + /** + * Endpoint pour récupérer la liste de tous les restaurants. + * @return une ResponseEntity contenant la liste des restaurants et le statut HTTP OK. + */ + @GetMapping + public ResponseEntity> getAllRestaurants() { + List restaurants = restaurantService.getAllRestaurants(); + return new ResponseEntity<>(restaurants, HttpStatus.OK); + } + + /** + * Endpoint pour récupérer un restaurant par son identifiant. + * @param id L'identifiant du restaurant. + * @return une ResponseEntity contenant le restaurant s'il est trouvé (OK), sinon NOT_FOUND. + */ + @GetMapping("/{id}") + public ResponseEntity getRestaurantById(@PathVariable Integer id) { + return restaurantService.getRestaurantById(id) + .map(restaurant -> new ResponseEntity<>(restaurant, HttpStatus.OK)) + .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } + + /** + * Endpoint pour créer un nouveau restaurant. + * @param restaurant Le restaurant à créer. + * @return une ResponseEntity contenant le restaurant créé et le statut CREATED. + */ + @PostMapping + public ResponseEntity createRestaurant(@RequestBody Restaurant restaurant) { + Restaurant savedRestaurant = restaurantService.saveRestaurant(restaurant); + return new ResponseEntity<>(savedRestaurant, HttpStatus.CREATED); + } + + /** + * Endpoint pour mettre à jour un restaurant existant. + * @param id L'identifiant du restaurant à mettre à jour. + * @param restaurantDetails Les nouvelles données du restaurant. + * @return une ResponseEntity contenant le restaurant mis à jour (OK), sinon NOT_FOUND. + */ + @PutMapping("/{id}") + public ResponseEntity updateRestaurant(@PathVariable Integer id, @RequestBody Restaurant restaurantDetails) { + return restaurantService.updateRestaurant(id, restaurantDetails) + .map(updateRestaurant -> new ResponseEntity<>(updateRestaurant, HttpStatus.OK)) + .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } + + /** + * Endpoint pour supprimer un restaurant. + * @param id L'identifiant du restaurant à supprimer. + * @return une ResponseEntity avec le statut NO_CONTENT si la suppression réussit, + * ou NOT_FOUND si le restaurant n'existe pas. + */ + @DeleteMapping("/{id}") + public ResponseEntity deleteRestaurant(@PathVariable Integer id) { + if (restaurantService.getRestaurantById(id).isPresent()) { + restaurantService.deleteRestaurant(id); + return ResponseEntity.noContent().build(); + } + return ResponseEntity.notFound().build(); + } +} diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/models/Restaurant.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/models/Restaurant.java index ea3d106..58d95eb 100644 --- a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/models/Restaurant.java +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/models/Restaurant.java @@ -13,7 +13,10 @@ import java.util.Collection; */ @Entity @Table(name = "restaurant") -@Data +@Getter +@Setter +@ToString(exclude = {"typerestaus", "preference"}) +@EqualsAndHashCode(exclude = {"typerestaus", "preference"}) @NoArgsConstructor @AllArgsConstructor @Builder diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/repository/PersonneRepository.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/repository/PersonneRepository.java new file mode 100644 index 0000000..b874bf9 --- /dev/null +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/repository/PersonneRepository.java @@ -0,0 +1,12 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.repository; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Personne; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface PersonneRepository extends JpaRepository { + Optional findByEmail(String email); +} diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/repository/PreferenceRepository.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/repository/PreferenceRepository.java new file mode 100644 index 0000000..75d08fc --- /dev/null +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/repository/PreferenceRepository.java @@ -0,0 +1,17 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.repository; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Preference; +import fr.vincent.ramiere.mangerautourdesimplonback.models.PreferencePK; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * Repository pour l'entité Preference. + * Fournit les méthodes pour les opérations CRUD sur les préférences. + */ +@Repository +public interface PreferenceRepository extends JpaRepository { + List findByPreferencePK_Personne_Id(Integer personneId); +} diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/repository/RestaurantRepository.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/repository/RestaurantRepository.java new file mode 100644 index 0000000..784d470 --- /dev/null +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/repository/RestaurantRepository.java @@ -0,0 +1,16 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Restaurant; + +/** + * Dépot pour l'entité Restaurant. + * Fournit les méthodes pour les opérations CRUD. + */ + +@Repository +public interface RestaurantRepository extends JpaRepository{ + +} diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/service/JpaUserDetailsService.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/service/JpaUserDetailsService.java new file mode 100644 index 0000000..ec70c2a --- /dev/null +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/service/JpaUserDetailsService.java @@ -0,0 +1,29 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.service; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Personne; +import fr.vincent.ramiere.mangerautourdesimplonback.repository.PersonneRepository; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service +public class JpaUserDetailsService implements UserDetailsService { + + private final PersonneRepository personneRepository; + + public JpaUserDetailsService(PersonneRepository personneRepository) { + this.personneRepository = personneRepository; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Personne personne = personneRepository.findByEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("Aucun utilisateur trouvé avec l'email: " + username)); + + return new org.springframework.security.core.userdetails.User( + personne.getEmail(), + personne.getPassword(), + personne.getRoleList()); + } +} diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/service/PreferenceService.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/service/PreferenceService.java new file mode 100644 index 0000000..843b9de --- /dev/null +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/service/PreferenceService.java @@ -0,0 +1,71 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.service; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Personne; +import fr.vincent.ramiere.mangerautourdesimplonback.models.Preference; +import fr.vincent.ramiere.mangerautourdesimplonback.models.PreferencePK; +import fr.vincent.ramiere.mangerautourdesimplonback.models.Restaurant; +import fr.vincent.ramiere.mangerautourdesimplonback.repository.PersonneRepository; +import fr.vincent.ramiere.mangerautourdesimplonback.repository.PreferenceRepository; +import fr.vincent.ramiere.mangerautourdesimplonback.repository.RestaurantRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +/** + * Service pour la gestion des préférences des utilisateurs. + */ +@Service +@AllArgsConstructor +public class PreferenceService { + + private final PreferenceRepository preferenceRepository; + private final PersonneRepository personneRepository; + private final RestaurantRepository restaurantRepository; + + /** + * Récupère toutes les préférences pour un utilisateur donné. + * + * @param personneId L'ID de l'utilisateur. + * @return Une liste de préférences. + */ + public List getPreferencesByPersonneId(Integer personneId) { + return preferenceRepository.findByPreferencePK_Personne_Id(personneId); + } + + /** + * Crée ou met à jour une préférence pour un utilisateur et un restaurant. + * + * @param personneId L'ID de l'utilisateur. + * @param restaurantId L'ID du restaurant. + * @param note La note attribuée (peut être null). + * @param favori Le statut de favori (peut être null). + * @return Un Optional contenant la préférence sauvegardée, ou un Optional vide + * si l'utilisateur ou le restaurant n'existe pas. + */ + @Transactional + public Optional saveOrUpdatePreference(Integer personneId, Integer restaurantId, Integer note, + Boolean favori) { + Optional personneOpt = personneRepository.findById(personneId); + Optional restaurantOpt = restaurantRepository.findById(restaurantId); + + if (personneOpt.isEmpty() || restaurantOpt.isEmpty()) { + return Optional.empty(); + } + + Personne personne = personneOpt.get(); + Restaurant restaurant = restaurantOpt.get(); + + PreferencePK preferencePK = new PreferencePK(personne, restaurant); + + Preference preference = preferenceRepository.findById(preferencePK) + .orElse(new Preference(preferencePK)); + + preference.setNote(note); + preference.setFavori(favori); + + return Optional.of(preferenceRepository.save(preference)); + } +} diff --git a/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/service/RestaurantService.java b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/service/RestaurantService.java new file mode 100644 index 0000000..c6e9aaf --- /dev/null +++ b/src/main/java/fr/vincent/ramiere/mangerautourdesimplonback/service/RestaurantService.java @@ -0,0 +1,78 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.service; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Restaurant; +import fr.vincent.ramiere.mangerautourdesimplonback.repository.RestaurantRepository; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import lombok.AllArgsConstructor; + +/** + * Service pour la gestion des restaurants. + * Contient la logique métier liée aux opérations sur les restaurants. + */ +@Service +@AllArgsConstructor +public class RestaurantService { + private final RestaurantRepository restaurantRepository; + + /** + * Récupère tous les restaurants. + * @return une liste de tous les restaurants. + */ + public List getAllRestaurants() { + return restaurantRepository.findAll(); + } + + /** + * Récupère un restaurant par son identifiant. + * @param id L'identifiant du restaurant. + * @return un Optional contenant le restaurant s'il est trouvé, sinon un Optional vide. + */ + public Optional getRestaurantById(Integer id) { + return restaurantRepository.findById(id); + } + + /** + * Enregistre un nouveau restaurant. + * @param restaurant Le restaurant à enregistrer. + * @return le restaurant enregistré + */ + public Restaurant saveRestaurant(Restaurant restaurant) { + return restaurantRepository.save(restaurant); + } + + /** + * Met à jour un restaurant existant. + * @param id L'identifiant du restaurant à mettre à jour. + * @param restaurantDetails Les nouvelles informations du restaurant. + * @return le restaurant mis à jour. + */ + public Optional updateRestaurant(Integer id, Restaurant restaurantDetails) { + return restaurantRepository.findById(id) + .map(existingRestaurant -> { + existingRestaurant.setNom(restaurantDetails.getNom()); + existingRestaurant.setAdresse(restaurantDetails.getAdresse()); + existingRestaurant.setTelephone(restaurantDetails.getTelephone()); + existingRestaurant.setAccesPMR(restaurantDetails.getAccesPMR()); + existingRestaurant.setAEmporter(restaurantDetails.getAEmporter()); + existingRestaurant.setSurPlace(restaurantDetails.getSurPlace()); + existingRestaurant.setPrix(restaurantDetails.getPrix()); + existingRestaurant.setLatitude(restaurantDetails.getLatitude()); + existingRestaurant.setLongitude(restaurantDetails.getLongitude()); + existingRestaurant.setWebsite(restaurantDetails.getWebsite()); + return restaurantRepository.save(existingRestaurant); + }); + } + + /** + * Supprime un restaurant par son identifiant. + * @param id L'identifiant du restaurant à supprimer. + */ + public void deleteRestaurant(Integer id) { + restaurantRepository.deleteById(id); + } +} diff --git a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/MangerAutourDeSimplonBackApplicationTests.java b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/MangerAutourDeSimplonBackApplicationTests.java index 9b4e06b..c8a4b6e 100644 --- a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/MangerAutourDeSimplonBackApplicationTests.java +++ b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/MangerAutourDeSimplonBackApplicationTests.java @@ -7,7 +7,8 @@ import org.springframework.boot.test.context.SpringBootTest; class MangerAutourDeSimplonBackApplicationTests { @Test - void testMain() { - MangerAutourDeSimplonBackApplication.main(new String[] {}); + void contextLoads() { + // Ce test garantit que le contexte de l'application Spring peut démarrer avec + // succès. } } diff --git a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/PreferenceControllerTest.java b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/PreferenceControllerTest.java new file mode 100644 index 0000000..de56c65 --- /dev/null +++ b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/PreferenceControllerTest.java @@ -0,0 +1,72 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.controller; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Preference; +import fr.vincent.ramiere.mangerautourdesimplonback.service.PreferenceService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import java.util.Collections; +import java.util.Optional; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class PreferenceControllerTest { + + private PreferenceService preferenceService; + private MockMvc mockMvc; + + @BeforeEach + void setup() { + preferenceService = Mockito.mock(PreferenceService.class); + PreferenceController controller = new PreferenceController(preferenceService); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void getPreferencesByPersonneId_shouldReturnOkAndCallService() throws Exception { + when(preferenceService.getPreferencesByPersonneId(42)).thenReturn(Collections.emptyList()); + + mockMvc.perform(get("/api/v1/preferences/user/42")) + .andExpect(status().isOk()); + + verify(preferenceService, times(1)).getPreferencesByPersonneId(42); + } + + @Test + void saveOrUpdatePreference_shouldReturnNotFound_whenServiceReturnsEmpty() throws Exception { + when(preferenceService.saveOrUpdatePreference(1, 2, 5, true)) + .thenReturn(Optional.empty()); + + String json = "{\"personneId\":1,\"restaurantId\":2,\"note\":5,\"favori\":true}"; + + mockMvc.perform(put("/api/v1/preferences") + .contentType(MediaType.APPLICATION_JSON) + .content(json)) + .andExpect(status().isNotFound()); + + verify(preferenceService, times(1)).saveOrUpdatePreference(1, 2, 5, true); + } + + @Test + void saveOrUpdatePreference_shouldReturnOk_whenServiceReturnsPreference() throws Exception { + Preference prefMock = Mockito.mock(Preference.class); + when(preferenceService.saveOrUpdatePreference(7, 8, 3, false)) + .thenReturn(Optional.of(prefMock)); + + String json = "{\"personneId\":7,\"restaurantId\":8,\"note\":3,\"favori\":false}"; + + mockMvc.perform(put("/api/v1/preferences") + .contentType(MediaType.APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()); + + verify(preferenceService, times(1)).saveOrUpdatePreference(7, 8, 3, false); + } +} diff --git a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/RestaurantControllerTest.java b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/RestaurantControllerTest.java new file mode 100644 index 0000000..dfec961 --- /dev/null +++ b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/controller/RestaurantControllerTest.java @@ -0,0 +1,120 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.controller; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Restaurant; +import fr.vincent.ramiere.mangerautourdesimplonback.service.RestaurantService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class RestaurantControllerTest { + + @Mock + private RestaurantService restaurantService; + + @InjectMocks + private RestaurantController restaurantController; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void getAllRestaurants_shouldReturnListOfRestaurants() { + Restaurant r1 = new Restaurant(); + Restaurant r2 = new Restaurant(); + List restaurants = Arrays.asList(r1, r2); + + when(restaurantService.getAllRestaurants()).thenReturn(restaurants); + + ResponseEntity> response = restaurantController.getAllRestaurants(); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(restaurants, response.getBody()); + } + + @Test + void getRestaurantById_found_shouldReturnRestaurant() { + Restaurant restaurant = new Restaurant(); + when(restaurantService.getRestaurantById(1)).thenReturn(Optional.of(restaurant)); + + ResponseEntity response = restaurantController.getRestaurantById(1); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(restaurant, response.getBody()); + } + + @Test + void getRestaurantById_notFound_shouldReturnNotFound() { + when(restaurantService.getRestaurantById(1)).thenReturn(Optional.empty()); + + ResponseEntity response = restaurantController.getRestaurantById(1); + + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + assertNull(response.getBody()); + } + + @Test + void createRestaurant_shouldReturnCreatedRestaurant() { + Restaurant restaurant = new Restaurant(); + Restaurant savedRestaurant = new Restaurant(); + when(restaurantService.saveRestaurant(restaurant)).thenReturn(savedRestaurant); + + ResponseEntity response = restaurantController.createRestaurant(restaurant); + + assertEquals(HttpStatus.CREATED, response.getStatusCode()); + assertEquals(savedRestaurant, response.getBody()); + } + + @Test + void updateRestaurant_found_shouldReturnUpdatedRestaurant() { + Restaurant details = new Restaurant(); + Restaurant updated = new Restaurant(); + when(restaurantService.updateRestaurant(eq(1), any(Restaurant.class))).thenReturn(Optional.of(updated)); + + ResponseEntity response = restaurantController.updateRestaurant(1, details); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(updated, response.getBody()); + } + + @Test + void updateRestaurant_notFound_shouldReturnNotFound() { + Restaurant details = new Restaurant(); + when(restaurantService.updateRestaurant(eq(1), any(Restaurant.class))).thenReturn(Optional.empty()); + + ResponseEntity response = restaurantController.updateRestaurant(1, details); + + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + assertNull(response.getBody()); + } + + @Test + void deleteRestaurant_found_shouldReturnNoContent() { + when(restaurantService.getRestaurantById(1)).thenReturn(Optional.of(new Restaurant())); + + ResponseEntity response = restaurantController.deleteRestaurant(1); + + assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); + verify(restaurantService, times(1)).deleteRestaurant(1); + } + + @Test + void deleteRestaurant_notFound_shouldReturnNotFound() { + when(restaurantService.getRestaurantById(1)).thenReturn(Optional.empty()); + + ResponseEntity response = restaurantController.deleteRestaurant(1); + + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + verify(restaurantService, never()).deleteRestaurant(anyInt()); + } +} diff --git a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PersonneTest.java b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PersonneTest.java index 3ac4cdf..06a821a 100644 --- a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PersonneTest.java +++ b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PersonneTest.java @@ -9,5 +9,8 @@ class PersonneTest { void testBuilder() { Personne p = Personne.builder().nom("Test").prenom("Test").email("t@t.fr").password("1234").build(); assertEquals("Test", p.getNom()); + assertEquals("Test", p.getPrenom()); + assertEquals("t@t.fr", p.getEmail()); + assertEquals("1234", p.getPassword()); } } diff --git a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PreferencePKTest.java b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PreferencePKTest.java new file mode 100644 index 0000000..0c83749 --- /dev/null +++ b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PreferencePKTest.java @@ -0,0 +1,48 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.models; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + +class PreferencePKTest { + + @Test + void noArgsConstructor_settersAndGetters_handleNulls() { + PreferencePK pk = new PreferencePK(); + // initially null + assertNull(pk.getPersonne()); + assertNull(pk.getRestau()); + + // set to null explicitly and verify + pk.setPersonne(null); + pk.setRestau(null); + assertNull(pk.getPersonne()); + assertNull(pk.getRestau()); + } + + @Test + void equals_and_hashCode_withBothFieldsNull_areEqual() { + PreferencePK a = new PreferencePK(); + PreferencePK b = new PreferencePK(); + + // Both have null personne and restau -> should be equal and have same hashCode + assertEquals(a, b); + assertEquals(b, a); + assertEquals(a.hashCode(), b.hashCode()); + + // reflexive + assertEquals(a, a); + } + + @Test + void equals_withSameReference_isTrue_and_hashCodeStable() { + PreferencePK original = new PreferencePK(); + PreferencePK sameRef = original; + + assertEquals(original, sameRef); + assertEquals(original.hashCode(), sameRef.hashCode()); + + int firstHash = original.hashCode(); + int secondHash = original.hashCode(); + assertEquals(firstHash, secondHash, "hashCode should be stable across invocations"); + } +} diff --git a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PreferenceTest.java b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PreferenceTest.java new file mode 100644 index 0000000..dc8d2e1 --- /dev/null +++ b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/PreferenceTest.java @@ -0,0 +1,65 @@ + +package fr.vincent.ramiere.mangerautourdesimplonback.models; + +import org.junit.jupiter.api.Test; +import java.io.*; +import static org.junit.jupiter.api.Assertions.*; + +class PreferenceTest { + + @Test + void defaultConstructor_and_setters_getters() { + Preference p = new Preference(); + assertNull(p.getPreferencePK()); + assertNull(p.getNote()); + assertNull(p.getFavori()); + + p.setNote(5); + p.setFavori(Boolean.TRUE); + + assertEquals(5, p.getNote()); + assertTrue(p.getFavori()); + } + + @Test + void allArgsConstructor_setsFieldsInOrder() { + // AllArgsConstructor order is (PreferencePK preferencePK, Integer note, Boolean + // favori) + Preference p = new Preference(null, 3, Boolean.FALSE); + + assertNull(p.getPreferencePK()); + assertEquals(3, p.getNote()); + assertFalse(p.getFavori()); + } + + @Test + void singleArgConstructor_setsOnlyPreferencePK() { + Preference p = new Preference((PreferencePK) null); + + assertNull(p.getPreferencePK()); + // other fields remain null when not provided + assertNull(p.getNote()); + assertNull(p.getFavori()); + } + + @Test + void implementsSerializable_and_canBeSerialized() throws Exception { + Preference original = new Preference(null, 4, Boolean.TRUE); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(original); + } + + byte[] bytes = bos.toByteArray(); + try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) { + Object obj = ois.readObject(); + assertTrue(obj instanceof Preference); + Preference deserialized = (Preference) obj; + + assertEquals(original.getPreferencePK(), deserialized.getPreferencePK()); + assertEquals(original.getNote(), deserialized.getNote()); + assertEquals(original.getFavori(), deserialized.getFavori()); + } + } +} diff --git a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/RestaurantTest.java b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/RestaurantTest.java new file mode 100644 index 0000000..fb96825 --- /dev/null +++ b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/models/RestaurantTest.java @@ -0,0 +1,26 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.models; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class RestaurantTest { + @Test + void testBuilder() { + Restaurant r = Restaurant.builder().nom("Test").adresse("Adresse Test").telephone("0123456789").accesPMR(true) + .aEmporter(true) + .surPlace(false).prix(10).latitude("1.0").longitude("2.0").website("site-test.com").build(); + assertEquals("Test", r.getNom()); + assertEquals("Adresse Test", r.getAdresse()); + assertEquals("0123456789", r.getTelephone()); + assertTrue(r.getAccesPMR()); + assertTrue(r.getAEmporter()); + assertFalse(r.getSurPlace()); + assertEquals(10, r.getPrix()); + assertEquals("1.0", r.getLatitude()); + assertEquals("2.0", r.getLongitude()); + assertEquals("site-test.com", r.getWebsite()); + } +} diff --git a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/services/PreferenceServiceTest.java b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/services/PreferenceServiceTest.java new file mode 100644 index 0000000..dc05244 --- /dev/null +++ b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/services/PreferenceServiceTest.java @@ -0,0 +1,116 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.services; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Personne; +import fr.vincent.ramiere.mangerautourdesimplonback.models.Preference; +import fr.vincent.ramiere.mangerautourdesimplonback.models.PreferencePK; +import fr.vincent.ramiere.mangerautourdesimplonback.models.Restaurant; +import fr.vincent.ramiere.mangerautourdesimplonback.repository.PersonneRepository; +import fr.vincent.ramiere.mangerautourdesimplonback.repository.PreferenceRepository; +import fr.vincent.ramiere.mangerautourdesimplonback.repository.RestaurantRepository; +import fr.vincent.ramiere.mangerautourdesimplonback.service.PreferenceService; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import java.util.List; +import java.util.Optional; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * Unit tests for PreferenceService. + */ +@ExtendWith(MockitoExtension.class) +class PreferenceServiceTest { + + @Mock + private PreferenceRepository preferenceRepository; + + @Mock + private PersonneRepository personneRepository; + + @Mock + private RestaurantRepository restaurantRepository; + + @InjectMocks + private PreferenceService preferenceService; + + @Test + void getPreferencesByPersonneId_returnsListFromRepository() { + Personne p = mock(Personne.class); + Restaurant r = mock(Restaurant.class); + Preference pref = new Preference(new PreferencePK(p, r)); + List expected = List.of(pref); + + when(preferenceRepository.findByPreferencePK_Personne_Id(1)).thenReturn(expected); + + List result = preferenceService.getPreferencesByPersonneId(1); + + assertSame(expected, result); + verify(preferenceRepository, times(1)).findByPreferencePK_Personne_Id(1); + } + + @Test + void saveOrUpdatePreference_whenPersonMissing_returnsEmpty() { + when(personneRepository.findById(1)).thenReturn(Optional.empty()); + // restaurant repo may or may not be called; service checks person first + Optional result = preferenceService.saveOrUpdatePreference(1, 2, 5, true); + + assertTrue(result.isEmpty()); + verify(personneRepository, times(1)).findById(1); + verifyNoInteractions(preferenceRepository); + } + + @Test + void saveOrUpdatePreference_createsNewPreference_andSavesIt() { + Personne personne = new Personne(); + Restaurant restaurant = new Restaurant(); + + when(personneRepository.findById(10)).thenReturn(Optional.of(personne)); + when(restaurantRepository.findById(20)).thenReturn(Optional.of(restaurant)); + when(preferenceRepository.findById(any(PreferencePK.class))).thenReturn(Optional.empty()); + when(preferenceRepository.save(any(Preference.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + Optional result = preferenceService.saveOrUpdatePreference(10, 20, 4, Boolean.TRUE); + + assertTrue(result.isPresent()); + Preference saved = result.get(); + // assume typical getter names getNote/getFavori exist on Preference + assertEquals(4, saved.getNote()); + assertEquals(Boolean.TRUE, saved.getFavori()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Preference.class); + verify(preferenceRepository, times(1)).save(captor.capture()); + Preference captured = captor.getValue(); + assertEquals(4, captured.getNote()); + assertEquals(Boolean.TRUE, captured.getFavori()); + } + + @Test + void saveOrUpdatePreference_updatesExistingPreference_andSavesIt() { + Personne personne = new Personne(); + Restaurant restaurant = new Restaurant(); + PreferencePK pk = new PreferencePK(personne, restaurant); + Preference existing = new Preference(pk); + existing.setNote(1); + existing.setFavori(false); + + when(personneRepository.findById(7)).thenReturn(Optional.of(personne)); + when(restaurantRepository.findById(8)).thenReturn(Optional.of(restaurant)); + when(preferenceRepository.findById(any(PreferencePK.class))).thenReturn(Optional.of(existing)); + when(preferenceRepository.save(any(Preference.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + Optional result = preferenceService.saveOrUpdatePreference(7, 8, 10, Boolean.TRUE); + + assertTrue(result.isPresent()); + Preference updated = result.get(); + assertSame(existing, updated); + assertEquals(10, updated.getNote()); + assertEquals(Boolean.TRUE, updated.getFavori()); + + verify(preferenceRepository, times(1)).save(existing); + } +} diff --git a/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/services/RestaurantServiceTest.java b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/services/RestaurantServiceTest.java new file mode 100644 index 0000000..85dd820 --- /dev/null +++ b/src/test/java/fr/vincent/ramiere/mangerautourdesimplonback/services/RestaurantServiceTest.java @@ -0,0 +1,131 @@ +package fr.vincent.ramiere.mangerautourdesimplonback.services; + +import fr.vincent.ramiere.mangerautourdesimplonback.models.Restaurant; +import fr.vincent.ramiere.mangerautourdesimplonback.repository.RestaurantRepository; +import fr.vincent.ramiere.mangerautourdesimplonback.service.RestaurantService; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; + +class RestaurantServiceTest { + @Mock + private RestaurantRepository restaurantRepository; + @InjectMocks + private RestaurantService restaurantService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testGetAllRestaurants() { + Restaurant r1 = new Restaurant(); + Restaurant r2 = new Restaurant(); + when(restaurantRepository.findAll()).thenReturn(Arrays.asList(r1, r2)); + + List result = restaurantService.getAllRestaurants(); + + assertEquals(2, result.size()); + verify(restaurantRepository).findAll(); + } + + @Test + void testGetRestaurantById_Found() { + Restaurant r = new Restaurant(); + r.setNom("Test"); + when(restaurantRepository.findById(1)).thenReturn(Optional.of(r)); + + Optional result = restaurantService.getRestaurantById(1); + + assertTrue(result.isPresent()); + assertEquals("Test", result.get().getNom()); + verify(restaurantRepository).findById(1); + } + + @Test + void testGetRestaurantById_NotFound() { + when(restaurantRepository.findById(2)).thenReturn(Optional.empty()); + + Optional result = restaurantService.getRestaurantById(2); + + assertFalse(result.isPresent()); + verify(restaurantRepository).findById(2); + } + + @Test + void testSaveRestaurant() { + Restaurant r = new Restaurant(); + r.setNom("SaveTest"); + when(restaurantRepository.save(r)).thenReturn(r); + + Restaurant result = restaurantService.saveRestaurant(r); + + assertEquals("SaveTest", result.getNom()); + verify(restaurantRepository).save(r); + } + + @Test + void testUpdateRestaurant_Found() { + Restaurant existing = new Restaurant(); + existing.setNom("Old"); + Restaurant details = new Restaurant(); + details.setNom("New"); + details.setAdresse("Adresse"); + details.setTelephone("123"); + details.setAccesPMR(true); + details.setAEmporter(true); + details.setSurPlace(false); + details.setPrix(10); + details.setLatitude("1.0"); + details.setLongitude("2.0"); + details.setWebsite("site"); + + when(restaurantRepository.findById(1)).thenReturn(Optional.of(existing)); + when(restaurantRepository.save(any(Restaurant.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + Optional result = restaurantService.updateRestaurant(1, details); + + assertTrue(result.isPresent()); + Restaurant updatedRestaurant = result.get(); + assertEquals("New", updatedRestaurant.getNom()); + assertEquals("Adresse", updatedRestaurant.getAdresse()); + assertEquals("123", updatedRestaurant.getTelephone()); + assertTrue(updatedRestaurant.getAccesPMR()); + assertTrue(updatedRestaurant.getAEmporter()); + assertFalse(updatedRestaurant.getSurPlace()); + assertEquals(10, updatedRestaurant.getPrix()); + assertEquals("1.0", updatedRestaurant.getLatitude()); + assertEquals("2.0", updatedRestaurant.getLongitude()); + assertEquals("site", updatedRestaurant.getWebsite()); + verify(restaurantRepository).findById(1); + verify(restaurantRepository).save(existing); + } + + @Test + void testUpdateRestaurant_NotFound() { + Restaurant details = new Restaurant(); + when(restaurantRepository.findById(99)).thenReturn(Optional.empty()); + + Optional result = restaurantService.updateRestaurant(99, details); + + assertFalse(result.isPresent()); + verify(restaurantRepository).findById(99); + verify(restaurantRepository, never()).save(any()); + } + + @Test + void testDeleteRestaurant() { + restaurantService.deleteRestaurant(5); + verify(restaurantRepository).deleteById(5); + } +}