findByStatus(Task.Status status) {
- return taskRepository.findAll().stream()
- .filter(task -> task.getStatus() == status)
- .toList();
+ return taskRepository.findByStatus(status.name());
}
}
\ No newline at end of file
diff --git a/NauJava/src/main/java/ru/andrmuratov/NauJava/service/UserService.java b/NauJava/src/main/java/ru/andrmuratov/NauJava/service/UserService.java
new file mode 100644
index 0000000..41a49e0
--- /dev/null
+++ b/NauJava/src/main/java/ru/andrmuratov/NauJava/service/UserService.java
@@ -0,0 +1,6 @@
+package ru.andrmuratov.NauJava.service;
+
+public interface UserService {
+ void createUserWithTask(String name, String email, String role, String taskTitle);
+ void deleteUserWithTasks(Long userId);
+}
\ No newline at end of file
diff --git a/NauJava/src/main/java/ru/andrmuratov/NauJava/service/UserServiceImpl.java b/NauJava/src/main/java/ru/andrmuratov/NauJava/service/UserServiceImpl.java
new file mode 100644
index 0000000..7da364b
--- /dev/null
+++ b/NauJava/src/main/java/ru/andrmuratov/NauJava/service/UserServiceImpl.java
@@ -0,0 +1,53 @@
+package ru.andrmuratov.NauJava.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.andrmuratov.NauJava.entity.Task;
+import ru.andrmuratov.NauJava.entity.User;
+import ru.andrmuratov.NauJava.repository.TaskRepository;
+import ru.andrmuratov.NauJava.repository.UserRepository;
+
+import java.time.LocalDateTime;
+
+@Service
+public class UserServiceImpl implements UserService {
+
+ private final UserRepository userRepository;
+ private final TaskRepository taskRepository;
+ private final PasswordEncoder passwordEncoder;
+
+ @Autowired
+ public UserServiceImpl(UserRepository userRepository,
+ TaskRepository taskRepository,
+ PasswordEncoder passwordEncoder) {
+ this.userRepository = userRepository;
+ this.taskRepository = taskRepository;
+ this.passwordEncoder = passwordEncoder;
+ }
+
+ @Override
+ @Transactional
+ public void createUserWithTask(String username, String email, String role, String taskTitle) {
+ User user = new User();
+ user.setUsername(username);
+ user.setEmail(email);
+ user.setRole(role);
+ user.setPassword(passwordEncoder.encode("defaultPassword"));
+ userRepository.save(user);
+
+ Task task = new Task();
+ task.setTitle(taskTitle);
+ task.setStatus("TODO");
+ task.setDeadline(LocalDateTime.now().plusDays(7));
+ task.setUser(user);
+ taskRepository.save(task);
+ }
+
+ @Override
+ @Transactional
+ public void deleteUserWithTasks(Long userId) {
+ userRepository.deleteById(userId);
+ }
+}
\ No newline at end of file
diff --git a/NauJava/src/main/resources/application.properties b/NauJava/src/main/resources/application.properties
index 8eb9f8b..b9c2b85 100644
--- a/NauJava/src/main/resources/application.properties
+++ b/NauJava/src/main/resources/application.properties
@@ -1,3 +1,14 @@
app.name=TaskFlow
-app.version=1.0.0
+app.version=2.0.0
+
+spring.datasource.url=jdbc:postgresql://localhost:5432/taskflow
+spring.datasource.username=postgres
+spring.datasource.password=naumen
+spring.datasource.driver-class-name=org.postgresql.Driver
+
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.show-sql=true
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
+spring.jpa.properties.hibernate.format_sql=true
+
management.endpoints.web.exposure.include=*
\ No newline at end of file
diff --git a/NauJava/src/main/resources/templates/login.html b/NauJava/src/main/resources/templates/login.html
new file mode 100644
index 0000000..47baec9
--- /dev/null
+++ b/NauJava/src/main/resources/templates/login.html
@@ -0,0 +1,34 @@
+
+
+
+
+ Login
+
+
+
+Login
+Invalid username or password
+You have been logged out
+
+Don't have an account? Register
+
+
\ No newline at end of file
diff --git a/NauJava/src/main/resources/templates/registration.html b/NauJava/src/main/resources/templates/registration.html
new file mode 100644
index 0000000..a84ad35
--- /dev/null
+++ b/NauJava/src/main/resources/templates/registration.html
@@ -0,0 +1,43 @@
+
+
+
+
+ Registration
+
+
+
+Registration
+
+
+Already have an account? Login
+
+
\ No newline at end of file
diff --git a/NauJava/src/main/resources/templates/report.html b/NauJava/src/main/resources/templates/report.html
new file mode 100644
index 0000000..b646c59
--- /dev/null
+++ b/NauJava/src/main/resources/templates/report.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Report
+
+
+Report Generation
+
+
+
+
+
\ No newline at end of file
diff --git a/NauJava/src/main/resources/templates/user-list.html b/NauJava/src/main/resources/templates/user-list.html
new file mode 100644
index 0000000..a6c4590
--- /dev/null
+++ b/NauJava/src/main/resources/templates/user-list.html
@@ -0,0 +1,40 @@
+
+
+
+
+ User List
+
+
+
+User List
+
+
+
+
+
+
+
+ | ID |
+ Name |
+ Email |
+ Role |
+
+
+ |
+ |
+ |
+ |
+
+
+
+Swagger UI |
+Actuator
+
+
\ No newline at end of file
diff --git a/NauJava/src/test/java/ru/andrmuratov/NauJava/NauJavaApplicationTests.java b/NauJava/src/test/java/ru/andrmuratov/NauJava/NauJavaApplicationTests.java
index f3c3b09..d6dd543 100644
--- a/NauJava/src/test/java/ru/andrmuratov/NauJava/NauJavaApplicationTests.java
+++ b/NauJava/src/test/java/ru/andrmuratov/NauJava/NauJavaApplicationTests.java
@@ -2,12 +2,13 @@
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
@SpringBootTest
+@ActiveProfiles("test")
class NauJavaApplicationTests {
@Test
void contextLoads() {
}
-
-}
+}
\ No newline at end of file
diff --git a/NauJava/src/test/java/ru/andrmuratov/NauJava/UserRepositoryTest.java b/NauJava/src/test/java/ru/andrmuratov/NauJava/UserRepositoryTest.java
new file mode 100644
index 0000000..3164eec
--- /dev/null
+++ b/NauJava/src/test/java/ru/andrmuratov/NauJava/UserRepositoryTest.java
@@ -0,0 +1,58 @@
+package ru.andrmuratov.NauJava;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.test.context.ActiveProfiles;
+import ru.andrmuratov.NauJava.entity.User;
+import ru.andrmuratov.NauJava.repository.UserRepository;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest
+@ActiveProfiles("test")
+class UserRepositoryTest {
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+
+ @Test
+ void testFindByUsername() {
+ String username = UUID.randomUUID().toString();
+ User user = new User();
+ user.setUsername(username);
+ user.setEmail(username + "@test.com");
+ user.setRole("USER");
+ user.setPassword(passwordEncoder.encode("testPassword"));
+ userRepository.save(user);
+
+ Optional found = userRepository.findByUsername(username);
+ assertThat(found).isPresent();
+ assertThat(found.get().getUsername()).isEqualTo(username);
+ }
+
+ @Test
+ void testCriteriaAPI() {
+ String username = UUID.randomUUID().toString();
+ String email = username + "@test.com";
+ User user = new User();
+ user.setUsername(username);
+ user.setEmail(email);
+ user.setRole("ADMIN");
+ user.setPassword(passwordEncoder.encode("testPassword"));
+ userRepository.save(user);
+
+ List found = userRepository.findByRoleAndEmailContaining("ADMIN", "@test.com");
+ Assertions.assertFalse(found.isEmpty());
+ Assertions.assertEquals(username, found.get(0).getUsername());
+ }
+}
\ No newline at end of file
diff --git a/NauJava/src/test/java/ru/andrmuratov/NauJava/UserServiceTest.java b/NauJava/src/test/java/ru/andrmuratov/NauJava/UserServiceTest.java
new file mode 100644
index 0000000..87daf37
--- /dev/null
+++ b/NauJava/src/test/java/ru/andrmuratov/NauJava/UserServiceTest.java
@@ -0,0 +1,35 @@
+package ru.andrmuratov.NauJava;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import ru.andrmuratov.NauJava.entity.User;
+import ru.andrmuratov.NauJava.repository.UserRepository;
+import ru.andrmuratov.NauJava.service.UserService;
+
+import java.util.UUID;
+
+@SpringBootTest
+class UserServiceTest {
+
+ @Autowired
+ private UserService userService;
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Test
+ void testCreateUserWithTask() {
+ String name = UUID.randomUUID().toString();
+ String email = name + "@test.com";
+ userService.createUserWithTask(name, email, "USER", "Test Task");
+
+ User found = userRepository.findByRoleJPQL("USER").stream()
+ .filter(u -> u.getEmail().equals(email))
+ .findFirst()
+ .orElse(null);
+ Assertions.assertNotNull(found);
+ Assertions.assertEquals(name, found.getUsername());
+ }
+}
\ No newline at end of file
diff --git a/NauJava/src/test/java/ru/andrmuratov/NauJava/api/UserApiTest.java b/NauJava/src/test/java/ru/andrmuratov/NauJava/api/UserApiTest.java
new file mode 100644
index 0000000..2875a08
--- /dev/null
+++ b/NauJava/src/test/java/ru/andrmuratov/NauJava/api/UserApiTest.java
@@ -0,0 +1,39 @@
+package ru.andrmuratov.NauJava.api;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+@SpringBootTest
+@AutoConfigureMockMvc
+class UserApiTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ void testGetRootEndpoint() throws Exception {
+ mockMvc.perform(get("/"))
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ void testGetUsers_Unauthorized() throws Exception {
+ mockMvc.perform(get("/users"))
+ .andExpect(status().is3xxRedirection());
+ }
+
+ @Test
+ @WithMockUser(username = "admin", roles = {"ADMIN"}) // <-- ДОБАВЛЕНО
+ void testActuatorEndpoint() throws Exception {
+ mockMvc.perform(get("/actuator"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._links").exists());
+ }
+}
\ No newline at end of file
diff --git a/NauJava/src/test/java/ru/andrmuratov/NauJava/service/UserServiceImplTest.java b/NauJava/src/test/java/ru/andrmuratov/NauJava/service/UserServiceImplTest.java
new file mode 100644
index 0000000..dd9aeab
--- /dev/null
+++ b/NauJava/src/test/java/ru/andrmuratov/NauJava/service/UserServiceImplTest.java
@@ -0,0 +1,73 @@
+package ru.andrmuratov.NauJava.service;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import ru.andrmuratov.NauJava.entity.User;
+import ru.andrmuratov.NauJava.repository.TaskRepository;
+import ru.andrmuratov.NauJava.repository.UserRepository;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class UserServiceImplTest {
+
+ @Mock
+ private UserRepository userRepository;
+
+ @Mock
+ private TaskRepository taskRepository;
+
+ @Mock
+ private PasswordEncoder passwordEncoder;
+
+ @InjectMocks
+ private UserServiceImpl userService;
+
+ private User testUser;
+
+ @BeforeEach
+ void setUp() {
+ testUser = new User();
+ testUser.setId(1L);
+ testUser.setUsername("testuser");
+ testUser.setPassword("encodedPassword");
+ testUser.setEmail("test@example.com");
+ testUser.setRole("USER");
+ }
+
+ @Test
+ void testCreateUserWithTask_Success() {
+ when(passwordEncoder.encode("defaultPassword")).thenReturn("encodedPassword");
+ when(userRepository.save(any(User.class))).thenReturn(testUser);
+ when(taskRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
+
+ assertDoesNotThrow(() -> userService.createUserWithTask("testuser", "test@example.com", "USER", "Test Task"));
+
+ verify(userRepository, times(1)).save(any(User.class));
+ verify(taskRepository, times(1)).save(any());
+ }
+
+ @Test
+ void testDeleteUserWithTasks_Success() {
+ doNothing().when(userRepository).deleteById(1L);
+
+ assertDoesNotThrow(() -> userService.deleteUserWithTasks(1L));
+
+ verify(userRepository, times(1)).deleteById(1L);
+ }
+
+ @Test
+ void testCreateUserWithTask_RepositoryThrowsException() {
+ when(passwordEncoder.encode("defaultPassword")).thenReturn("encodedPassword");
+ when(userRepository.save(any(User.class))).thenThrow(new RuntimeException("Database error"));
+
+ assertThrows(RuntimeException.class, () -> userService.createUserWithTask("testuser", "test@example.com", "USER", "Test Task"));
+ }
+}
\ No newline at end of file
diff --git a/NauJava/src/test/java/ru/andrmuratov/NauJava/ui/LoginUiTest.java b/NauJava/src/test/java/ru/andrmuratov/NauJava/ui/LoginUiTest.java
new file mode 100644
index 0000000..e20886d
--- /dev/null
+++ b/NauJava/src/test/java/ru/andrmuratov/NauJava/ui/LoginUiTest.java
@@ -0,0 +1,137 @@
+package ru.andrmuratov.NauJava.ui;
+
+import io.github.bonigarcia.wdm.WebDriverManager;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.test.context.ActiveProfiles;
+import ru.andrmuratov.NauJava.entity.User;
+import ru.andrmuratov.NauJava.repository.UserRepository;
+
+import java.time.Duration;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+class LoginUiTest {
+
+ @LocalServerPort
+ private int port;
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+
+ private WebDriver driver;
+ private WebDriverWait wait;
+ private String baseUrl;
+ private User testUser;
+
+ @BeforeEach
+ void setUp() {
+ WebDriverManager.chromedriver().setup();
+ driver = new ChromeDriver();
+ wait = new WebDriverWait(driver, Duration.ofSeconds(10));
+ baseUrl = "http://localhost:" + port;
+ driver.manage().window().maximize();
+
+ // Создаем тестового пользователя
+ testUser = new User();
+ testUser.setUsername("testuser");
+ testUser.setPassword(passwordEncoder.encode("testpass123"));
+ testUser.setEmail("test@example.com");
+ testUser.setRole("USER");
+ userRepository.save(testUser);
+ }
+
+ @AfterEach
+ void tearDown() {
+ if (driver != null) {
+ driver.quit();
+ }
+ // Очищаем тестовые данные
+ if (testUser != null && testUser.getId() != null) {
+ userRepository.delete(testUser);
+ }
+ }
+
+ @Test
+ void testSuccessfulLogin() {
+ driver.get(baseUrl + "/login");
+
+ WebElement usernameInput = wait.until(ExpectedConditions.visibilityOfElementLocated(By.name("username")));
+ WebElement passwordInput = driver.findElement(By.name("password"));
+ WebElement loginButton = driver.findElement(By.cssSelector("button[type='submit']"));
+
+ usernameInput.clear();
+ usernameInput.sendKeys("testuser");
+
+ passwordInput.clear();
+ passwordInput.sendKeys("testpass123");
+
+ loginButton.click();
+
+ wait.until(ExpectedConditions.urlContains("/users/list"));
+
+ String currentUrl = driver.getCurrentUrl();
+ assertTrue(currentUrl.contains("/users/list"), "Should be redirected to user list page");
+
+ WebElement pageTitle = wait.until(ExpectedConditions.visibilityOfElementLocated(By.tagName("h1")));
+ assertEquals("User List", pageTitle.getText());
+ }
+
+ @Test
+ void testLogout() {
+ // Сначала входим
+ driver.get(baseUrl + "/login");
+
+ WebElement usernameInput = wait.until(ExpectedConditions.visibilityOfElementLocated(By.name("username")));
+ WebElement passwordInput = driver.findElement(By.name("password"));
+ WebElement loginButton = driver.findElement(By.cssSelector("button[type='submit']"));
+
+ usernameInput.sendKeys("testuser");
+ passwordInput.sendKeys("testpass123");
+ loginButton.click();
+
+ wait.until(ExpectedConditions.urlContains("/users/list"));
+
+ // Ищем кнопку logout (обычно это форма с кнопкой)
+ WebElement logoutButton = wait.until(ExpectedConditions.elementToBeClickable(
+ By.cssSelector("button[type='submit']")
+ ));
+
+ logoutButton.click();
+
+ wait.until(ExpectedConditions.urlContains("/login"));
+
+ String currentUrl = driver.getCurrentUrl();
+ assertTrue(currentUrl.contains("/login"), "Should be redirected to login page");
+ }
+
+ @Test
+ void testLoginPageLoads() {
+ driver.get(baseUrl + "/login");
+
+ String pageTitle = driver.getTitle();
+ assertTrue(pageTitle.contains("Login") || pageTitle.contains("login"), "Page should have login in title");
+
+ WebElement usernameInput = wait.until(ExpectedConditions.visibilityOfElementLocated(By.name("username")));
+ WebElement passwordInput = driver.findElement(By.name("password"));
+
+ assertNotNull(usernameInput, "Username input should be present");
+ assertNotNull(passwordInput, "Password input should be present");
+ }
+}
\ No newline at end of file
diff --git a/NauJava/src/test/resources/application-test.properties b/NauJava/src/test/resources/application-test.properties
new file mode 100644
index 0000000..8995a28
--- /dev/null
+++ b/NauJava/src/test/resources/application-test.properties
@@ -0,0 +1,6 @@
+spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=PostgreSQL
+spring.datasource.driver-class-name=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+spring.jpa.hibernate.ddl-auto=create-drop
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
\ No newline at end of file