commit d60d25d04b7aa230a92a428f4fd9fe7c8332bb1a
Author: Jiri Trefil <badjokke@gmail.com>
Date:   Thu Apr 6 18:08:53 2023 +0200

    #10244 završení implementace service a controllerů. Sepsány jednotkové testy.

diff --git a/pom.xml b/pom.xml
index 9a002fe..f7627e8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,6 +93,8 @@
 		</dependency>
 
 
+
+
 		<!-- Junit libraries for unit tests-->
 		<dependency>
 			<groupId>org.junit.jupiter</groupId>
@@ -122,6 +124,12 @@
 			<version>5.2.0</version>
 			<scope>test</scope>
 		</dependency>
+		<!-- Spring TESTING -->
+		<dependency>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-test</artifactId>
+			<scope>test</scope>
+		</dependency>
 
 	</dependencies>
 
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/controller/UserController.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/controller/UserController.java
index b6dd69d..de6238b 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/controller/UserController.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/controller/UserController.java
@@ -1,5 +1,6 @@
 package cz.zcu.fav.kiv.antipatterndetectionapp.v2.controller;
 
+import cz.zcu.fav.kiv.antipatterndetectionapp.v2.dials.UserModelStatusCodes;
 import cz.zcu.fav.kiv.antipatterndetectionapp.v2.model.User;
 import cz.zcu.fav.kiv.antipatterndetectionapp.v2.service.UserService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -26,9 +27,9 @@ public class UserController {
      * @return      message
      */
     @PostMapping(value = "/register")
-    public String registerUser(@RequestBody User user) {
-        int statusCode = userService.registerUser(user);
-        return "user register";
+    public ResponseEntity<String> registerUser(@RequestBody User user) {
+        UserModelStatusCodes statusCode = userService.registerUser(user);
+        return getResponseEntity(statusCode);
     }
 
     /**
@@ -37,9 +38,9 @@ public class UserController {
      * @return      message
      */
     @PostMapping(value = "/login")
-    public String loginUser(@RequestBody User user) {
-        int statusCode = userService.loginUser(user);
-        return "user logged";
+    public ResponseEntity<String> loginUser(@RequestBody User user) {
+        UserModelStatusCodes statusCode = userService.loginUser(user);
+        return getResponseEntity(statusCode);
     }
 
     /**
@@ -48,9 +49,34 @@ public class UserController {
      * @return      message after logout
      */
     @PostMapping(value = "/logout")
-    public String logoutUser(@RequestBody User user) {
-        int statusCode = userService.logoutUser(user);
-        return "user logout";
+    public ResponseEntity<String> logoutUser(@RequestBody User user) {
+        UserModelStatusCodes statusCode = userService.logoutUser(user);
+        return getResponseEntity(statusCode);
     }
 
+    /**
+     * Method to create response
+     * @param statusCode    UserModelStatusCodes code
+     * @return              ResponseEntity with code and msg
+     */
+    private ResponseEntity<String> getResponseEntity(UserModelStatusCodes statusCode) {
+        String message = this.generateResponseObject(statusCode);
+        int code = statusCode.statusCode;
+        return new ResponseEntity<>(message, HttpStatus.valueOf(code));
+    }
+
+    /**
+     * Method to create JSON object
+     * @param code  UserModelStatusCodes code
+     * @return      String that represents JSON object
+     */
+    private String generateResponseObject(UserModelStatusCodes code){
+        HashMap<String, String> json = new HashMap<>();
+        json.put("msg", code.label);
+        return JSONBuilder.buildJson(json);
+
+    }
+
+
+
 }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/dials/UserModelStatusCodes.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/dials/UserModelStatusCodes.java
new file mode 100644
index 0000000..d83c63d
--- /dev/null
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/dials/UserModelStatusCodes.java
@@ -0,0 +1,23 @@
+package cz.zcu.fav.kiv.antipatterndetectionapp.v2.dials;
+
+/**
+ * Enum for status codes
+ */
+public enum UserModelStatusCodes {
+    INVALID_USER_ARGUMENTS("Invalid user arguments for registration",400),
+    USER_EXISTS("User exists",400),
+    HASH_FAILED("Error occurred while creating user",500),
+    USER_CREATED("User successfully created",201),
+    USER_LOGGED_IN("User logged in successfully",200),
+    USER_LOGIN_FAILED("User login failed",401),
+    USER_CREATION_FAILED("Error occurred while creating user",503),
+    USER_LOGGED_OUT("User logged out.",200);
+
+    public final String label;
+    public final int statusCode;
+
+    private UserModelStatusCodes(String s, int i) {
+        this.label = s;
+        this.statusCode = i;
+    }
+}
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/model/User.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/model/User.java
index 67e73be..9a577be 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/model/User.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/model/User.java
@@ -34,6 +34,12 @@ public class User {
     @Column(length = MAX_COLUMN_LENGTH)
     private String email;
 
+    /**
+     * Password of the User
+     */
+    @Column(length = MAX_COLUMN_LENGTH)
+    private String password;
+
     public User() {
     }
 
@@ -42,6 +48,13 @@ public class User {
         this.email = email;
     }
 
+    public User(String name, String email, String password){
+        this.name = name;
+        this.email = email;
+        this.password = password;
+    }
+
+
     /**
      * Getter of id
      * @return id of the user
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserService.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserService.java
index c9b0b9a..cddf913 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserService.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserService.java
@@ -1,5 +1,6 @@
 package cz.zcu.fav.kiv.antipatterndetectionapp.v2.service;
 
+import cz.zcu.fav.kiv.antipatterndetectionapp.v2.dials.UserModelStatusCodes;
 import cz.zcu.fav.kiv.antipatterndetectionapp.v2.model.User;
 
 /**
@@ -13,19 +14,19 @@ public interface UserService {
      * @param user serialized JSON object representing user
      * @return Integer - status code of the operation, ie 1 - successful, 0 - name taken, ...
      */
-    public int registerUser(User user);
+    public UserModelStatusCodes registerUser(User user);
     /**
      * Method attempts to log in a user and returns status code indicating login result
      * @param user serialized JSON object representing user
      * @return Integer - status code of the operation, ie 1 - successful, 0 - failed, ....
      */
-    public int loginUser(User user);
+    public UserModelStatusCodes loginUser(User user);
     /**
      * Method attempts to log out a user
      * @param user serialized JSON object representing user
      * @return Integer - status code of the operation, ie 1 - successful, 0 - failed, ....
      */
-    public int logoutUser(User user);
+    public UserModelStatusCodes logoutUser(User user);
 
 
 }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserServiceImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserServiceImpl.java
index bf584f8..7917e7c 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserServiceImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserServiceImpl.java
@@ -1,10 +1,16 @@
 package cz.zcu.fav.kiv.antipatterndetectionapp.v2.service;
 
+import cz.zcu.fav.kiv.antipatterndetectionapp.v2.dials.UserModelStatusCodes;
 import cz.zcu.fav.kiv.antipatterndetectionapp.v2.model.User;
 import cz.zcu.fav.kiv.antipatterndetectionapp.v2.repository.UserRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.regex.Pattern;
+
 /**
  * Service provides functionality for user login/register/logout
  * @author Vaclav Hrabik, Jiri Trefil
@@ -25,23 +31,71 @@ public class UserServiceImpl implements UserService {
      *
      */
     @Override
-    public int registerUser(User user) {
+    public UserModelStatusCodes registerUser(User user) {
         final String email = user.getEmail();
         final String name = user.getName();
-        //if client request violates constraints - kill the request. User will not be registered.
-        if (email.length() > User.getEmailConstraint() || name.length() > User.getNameConstraint()) {
-            return 0;
-        }
-        //if the username is taken, kill the request aswell
+        final String password = user.getPassword();
+
+        if (!verifyUserParameters(name, email, password))
+            return UserModelStatusCodes.INVALID_USER_ARGUMENTS;
+
+        //if the username is taken, kill the request as well
         if (userRepository.findByName(user.getName()) != null) {
-            return -1;
+            return UserModelStatusCodes.USER_EXISTS;
+        }
+
+        String passwordHash = hashPassword(password);
+        //server side fault
+        if (passwordHash == null) {
+            return UserModelStatusCodes.HASH_FAILED;
         }
         //save the user
         User u = userRepository.save(user);
 
         //TODO request to OAuth for token - send user info to the oauth app for token
         //return okay status code, the user was created
-        return 1;
+        return UserModelStatusCodes.USER_CREATED;
+    }
+
+    /**
+     * @param email String - user email provided by client
+     * @param name String - username provided by client
+     * @return true if @param User is valid
+     */
+    private boolean verifyUserParameters(String name, String email, String password) {
+        //if client send somehow not whole register request.
+        if (email == null || name == null || password == null) {
+            return false;
+        }
+        //if client request violates constraints - kill the request. User will not be registered.
+        if (email.length() > User.getEmailConstraint() || name.length() > User.getNameConstraint()) {
+            return false;
+        }
+        //regex for email validation
+        String emailRegex = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$";
+        final Pattern emailPattern = Pattern.compile(emailRegex);
+        //return true if email is valid (pattern wise)
+        return emailPattern.matcher(email).find();
+    }
+
+    /**
+     * Method to hash password
+     * @param password  password from client
+     * @return          hashed password for database
+     */
+    private String hashPassword(String password) {
+        //standard java security encryption module
+        MessageDigest digest = null;
+        try{
+            //try to instance the class - throws an error if algorithm
+            digest = MessageDigest.getInstance("SHA3-256");
+        }
+        catch(NoSuchAlgorithmException exception){
+            exception.printStackTrace();
+            return null;
+        }
+        byte [] tmp = digest.digest(password.getBytes(StandardCharsets.UTF_8));
+        return new String(tmp,StandardCharsets.UTF_8);
     }
 
     /**
@@ -52,20 +106,20 @@ public class UserServiceImpl implements UserService {
      *      1 - if user is logged successfully
      */
     @Override
-    public int loginUser(User user) {
+    public UserModelStatusCodes loginUser(User user) {
         User u = userRepository.findByName(user.getName());
         //TODO request to OAuth for authentication
-        return u == null ? 0 : 1;
+        return u == null ? UserModelStatusCodes.USER_LOGIN_FAILED : UserModelStatusCodes.USER_LOGGED_IN;
     }
 
     /**
      * Methods invalidates JWT token in OAuth application
      * @param user serialized JSON object representing user
-     * @return
+     * @return UserModelStatusCodes user logged out status flag
      */
     @Override
-    public int logoutUser(User user) {
+    public UserModelStatusCodes logoutUser(User user) {
         //TODO with OAuth app
-        return 0;
+        return UserModelStatusCodes.USER_LOGGED_OUT;
     }
 }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/utils/JSONBuilder.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/utils/JSONBuilder.java
new file mode 100644
index 0000000..27ef4d5
--- /dev/null
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/utils/JSONBuilder.java
@@ -0,0 +1,30 @@
+package cz.zcu.fav.kiv.antipatterndetectionapp.v2.utils;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.util.HashMap;
+
+/**
+ * Version 1.0 - only simple json can be built
+ * @version 1.0
+ * @author Vaclav Hrabik, Jiri Trefil
+ */
+public class JSONBuilder {
+
+    /**
+     * Method transforms map into string representation of JSON object
+     * @param map key value pair that will be generated as json line
+     * @return String representation of JSON object
+     */
+    public static String buildJson(HashMap<String, String> map){
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode jsonObject = mapper.createObjectNode();
+        for (String key : map.keySet()) {
+            jsonObject.put(key,map.get(key));
+        }
+        return jsonObject.toString();
+    }
+
+
+
+}
diff --git a/src/test/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/controller/UserControllerTest.java b/src/test/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/controller/UserControllerTest.java
index 6123b9d..a802c4e 100644
--- a/src/test/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/controller/UserControllerTest.java
+++ b/src/test/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/controller/UserControllerTest.java
@@ -1,14 +1,25 @@
 package cz.zcu.fav.kiv.antipatterndetectionapp.v2.controller;
 
+import cz.zcu.fav.kiv.antipatterndetectionapp.v2.dials.UserModelStatusCodes;
 import cz.zcu.fav.kiv.antipatterndetectionapp.v2.service.UserService;
-import cz.zcu.fav.kiv.antipatterndetectionapp.v2.service.UserServiceImpl;
+import cz.zcu.fav.kiv.antipatterndetectionapp.v2.utils.JSONBuilder;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mockito;
 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.security.test.context.support.WithMockUser;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.RequestBuilder;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+
+import java.util.HashMap;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 
 @ExtendWith(SpringExtension.class)
 @WebMvcTest(value = UserController.class)
@@ -22,4 +33,127 @@ public class UserControllerTest {
     private UserService userService;
 
 
+    @Test
+    public void registerUserNew() throws Exception {
+        Mockito.when(userService.registerUser(any())).thenReturn(UserModelStatusCodes.USER_CREATED);
+        HashMap<String,String> map = new HashMap<>();
+        map.put("name","pepa");
+        map.put("password","ahojSvete");
+        map.put("email","yxz@ahoj.cz");
+        String json = JSONBuilder.buildJson(map);
+        RequestBuilder requestBuilder = MockMvcRequestBuilders
+                .post("/v2/user/register")
+                .accept(MediaType.APPLICATION_JSON).content(json)
+                .contentType(MediaType.APPLICATION_JSON);
+
+        MvcResult result = mockMvc.perform(requestBuilder).andReturn();
+
+        MockHttpServletResponse response = result.getResponse();
+
+        assertEquals(HttpStatus.CREATED.value(), response.getStatus());
+    }
+    @Test
+    public void registerUserInvalidArguments() throws Exception {
+        Mockito.when(userService.registerUser(any())).thenReturn(UserModelStatusCodes.INVALID_USER_ARGUMENTS);
+        HashMap<String,String> map = new HashMap<>();
+        map.put("name","pepa");
+        map.put("password","ahojSvete");
+        map.put("email","yxz");
+        String json = JSONBuilder.buildJson(map);
+        RequestBuilder requestBuilder = MockMvcRequestBuilders
+                .post("/v2/user/register")
+                .accept(MediaType.APPLICATION_JSON).content(json)
+                .contentType(MediaType.APPLICATION_JSON);
+
+        MvcResult result = mockMvc.perform(requestBuilder).andReturn();
+
+        MockHttpServletResponse response = result.getResponse();
+
+        assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
+    }
+
+
+
+    @Test
+    public void registerUserExists() throws Exception {
+        Mockito.when(userService.registerUser(any())).thenReturn(UserModelStatusCodes.USER_EXISTS);
+        HashMap<String,String> map = new HashMap<>();
+        map.put("name","pepa");
+        map.put("password","ahojSvete");
+        map.put("email","yxz@ahoj.cz");
+        String json = JSONBuilder.buildJson(map);
+        RequestBuilder requestBuilder = MockMvcRequestBuilders
+                .post("/v2/user/register")
+                .accept(MediaType.APPLICATION_JSON).content(json)
+                .contentType(MediaType.APPLICATION_JSON);
+
+        MvcResult result = mockMvc.perform(requestBuilder).andReturn();
+
+        MockHttpServletResponse response = result.getResponse();
+
+        assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
+    }
+
+    @Test
+    public void loginValidUser() throws Exception {
+        Mockito.when(userService.loginUser(any())).thenReturn(UserModelStatusCodes.USER_LOGGED_IN);
+        HashMap<String,String> map = new HashMap<>();
+        map.put("name","pepa");
+        map.put("password","ahojSvete");
+        map.put("email","yxz@ahoj.cz");
+        String json = JSONBuilder.buildJson(map);
+        RequestBuilder requestBuilder = MockMvcRequestBuilders
+              .post("/v2/user/login")
+              .accept(MediaType.APPLICATION_JSON).content(json)
+              .contentType(MediaType.APPLICATION_JSON);
+
+        MvcResult result = mockMvc.perform(requestBuilder).andReturn();
+
+        MockHttpServletResponse response = result.getResponse();
+
+        assertEquals(HttpStatus.OK.value(), response.getStatus());
+    }
+
+    @Test
+    public void loginInValidUser() throws Exception {
+        Mockito.when(userService.loginUser(any())).thenReturn(UserModelStatusCodes.USER_LOGIN_FAILED);
+        HashMap<String,String> map = new HashMap<>();
+        map.put("name","pepa");
+        map.put("password","ahojSvete");
+        map.put("email","yxz@ahoj.cz");
+        String json = JSONBuilder.buildJson(map);
+        RequestBuilder requestBuilder = MockMvcRequestBuilders
+                .post("/v2/user/login")
+                .accept(MediaType.APPLICATION_JSON).content(json)
+                .contentType(MediaType.APPLICATION_JSON);
+
+        MvcResult result = mockMvc.perform(requestBuilder).andReturn();
+
+        MockHttpServletResponse response = result.getResponse();
+
+        assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatus());
+    }
+
+    @Test
+    public void logoutUser() throws Exception {
+        Mockito.when(userService.logoutUser(any())).thenReturn(UserModelStatusCodes.USER_LOGGED_OUT);
+        HashMap<String,String> map = new HashMap<>();
+        map.put("name","pepa");
+        map.put("password","ahojSvete");
+        map.put("email","yxz");
+        String json = JSONBuilder.buildJson(map);
+        RequestBuilder requestBuilder = MockMvcRequestBuilders
+                .post("/v2/user/logout")
+                .accept(MediaType.APPLICATION_JSON).content(json)
+                .contentType(MediaType.APPLICATION_JSON);
+
+        MvcResult result = mockMvc.perform(requestBuilder).andReturn();
+
+        MockHttpServletResponse response = result.getResponse();
+
+        assertEquals(HttpStatus.OK.value(), response.getStatus());
+    }
+
+
+
 }
diff --git a/src/test/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserServiceImpTest.java b/src/test/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserServiceImpTest.java
index 2811250..618648b 100644
--- a/src/test/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserServiceImpTest.java
+++ b/src/test/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/UserServiceImpTest.java
@@ -15,6 +15,8 @@ import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 
 /**
  * Tests for userServiceImplementation
@@ -39,6 +41,8 @@ public class UserServiceImpTest {
     @Autowired
     private UserService userService;
 
+
+
     /**
      * Mocked userRepository
      */
@@ -153,11 +157,12 @@ public class UserServiceImpTest {
     @Test
     public void whenUserIsNotRegistered_thenReturnMSG() {
 
-        Mockito.when(userRepository.findByName(mockUser.getName())).thenReturn(mockUser);
+        //Mockito.when(userRepository.findByName(mockUser.getName())).thenReturn(mockUser);
 
         String name = "notest";
         int foundCode = userService.loginUser(new User(name, ""));
 
         assertEquals(foundCode, 0);
     }
+
 }
