diff --git a/pom.xml b/pom.xml index 3d8377c..d940a6d 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,11 @@ postgresql test + + org.springframework.security + spring-security-test + test + @@ -73,7 +78,17 @@ org.springframework.boot spring-boot-maven-plugin + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 10 + 10 + UTF-8 + + - + \ No newline at end of file diff --git a/src/test/java/com/skypro/simplebanking/SimpleBankingApplicationTests.java b/src/test/java/com/skypro/simplebanking/SimpleBankingApplicationTests.java index 1d4dd66..8518939 100644 --- a/src/test/java/com/skypro/simplebanking/SimpleBankingApplicationTests.java +++ b/src/test/java/com/skypro/simplebanking/SimpleBankingApplicationTests.java @@ -1,9 +1,38 @@ package com.skypro.simplebanking; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import javax.sql.DataSource; + @SpringBootTest +@Testcontainers class SimpleBankingApplicationTests { -} + @Container + private static final PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:13") + .withUsername("postgres") + .withPassword("postgres"); + + @DynamicPropertySource + static void postgresProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + } + + @Autowired + private DataSource dataSource; + + @Test + void contextLoads() { + } + +} \ No newline at end of file diff --git a/src/test/java/com/skypro/simplebanking/controller/UserControllerTest.java b/src/test/java/com/skypro/simplebanking/controller/UserControllerTest.java new file mode 100644 index 0000000..af0af29 --- /dev/null +++ b/src/test/java/com/skypro/simplebanking/controller/UserControllerTest.java @@ -0,0 +1,250 @@ +package com.skypro.simplebanking.controller; + +import com.skypro.simplebanking.dto.AccountDTO; +import com.skypro.simplebanking.dto.BalanceChangeRequest; +import com.skypro.simplebanking.dto.BankingUserDetails; +import com.skypro.simplebanking.entity.Account; +import com.skypro.simplebanking.entity.User; +import com.skypro.simplebanking.repository.AccountRepository; +import com.skypro.simplebanking.repository.UserRepository; +import com.skypro.simplebanking.service.UserService; +import net.minidev.json.JSONObject; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.security.core.Authentication; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.Base64Utils; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import javax.sql.DataSource; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@SpringBootTest +@AutoConfigureMockMvc +@Testcontainers +public class UserControllerTest { + @Autowired + MockMvc mockMvc; + @Autowired + private DataSource dataSource; + @Container + private static final PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:latest") + .withUsername("postgres") + .withPassword("postgres"); + + @DynamicPropertySource + static void postgresProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + } + + + @Autowired + private UserRepository userRepository; + @Autowired + private AccountRepository accountRepository; + @Autowired + private UserService userService; + + + @BeforeEach + void createRepository() { + userService.createUser("username1","password1" ); + userService.createUser("username2","password2" ); + userService.createUser("username3","password3" ); + userService.createUser("username4","password4" ); + userService.createUser("username5","password5" ); + } + + @AfterEach + void cleanRepository() { + userRepository.deleteAll(); + accountRepository.deleteAll(); + } + + @Test + void testPostgresql() throws SQLException { + try (Connection conn = dataSource.getConnection()) { + assertThat(conn).isNotNull(); + } + } + + @Test + void getTranzaction_Test_OK() throws Exception { + JSONObject transfer = new JSONObject(); + transfer.put("fromAccountId", getAccountIdByUsername("username1")); + transfer.put("toUserId", getUserIdByUserName("username2")); + transfer.put("toAccountId", getAccountIdByUsername("username2")); + transfer.put("amount", 1); + + mockMvc.perform(post("/transfer") + .header(HttpHeaders.AUTHORIZATION, getBasicAuthenticationHeader("username1", "password1")) + .contentType(MediaType.APPLICATION_JSON) + .content(transfer.toString())) + .andExpect(status().isOk()); + } + + @Test + @WithMockUser(roles = "USER") + void givenUsers_OK() throws Exception { + mockMvc.perform(get("/user/list")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").isNotEmpty()) + .andExpect(jsonPath("$.length()").value(5)); + } + + + @Test + @WithMockUser(roles = "USER") + void givenNoBody_whenEmptyJsonArray() throws Exception { + userRepository.deleteAll(); + mockMvc.perform(get("/user/list")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").isEmpty()); + } + + @Test + @WithMockUser(roles = "ADMIN") + void givenUsers_AdminNoAccess_Error403() throws Exception { + mockMvc.perform(get("/user/list")) + .andExpect(status().is4xxClientError()); + } + + private String getBasicAuthenticationHeader(String username, String password) { + return "Basic " + Base64Utils.encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8)); + } + + @Test + void getTranzaction_Test_WrongAccountCurrency_Status400() throws Exception { + + JSONObject transfer = new JSONObject(); + transfer.put("fromAccountId", 1); + transfer.put("toUserId", 2); + transfer.put("toAccountId", 5); + transfer.put("amount", 1); + + mockMvc.perform(post("/transfer") + .header(HttpHeaders.AUTHORIZATION, getBasicAuthenticationHeader("username1", "password1")) + .contentType(MediaType.APPLICATION_JSON) + .content(transfer.toString())) + .andExpect(status().is4xxClientError()); + } + + @Test + void getTranzaction_Test_AccountNotFoundException() throws Exception { + + JSONObject transfer = new JSONObject(); + transfer.put("fromAccountId", 1); + transfer.put("toUserId", 2); + transfer.put("toAccountId", 44); + transfer.put("amount", 1); + + mockMvc.perform(post("/transfer") + .header(HttpHeaders.AUTHORIZATION, getBasicAuthenticationHeader("username1", "password1")) + .contentType(MediaType.APPLICATION_JSON) + .content(transfer.toString())) + .andExpect(status().is4xxClientError()); + } + + @Test + void getMyProfile_Test_OK() throws Exception { + mockMvc.perform(get("/user/me") + .header(HttpHeaders.AUTHORIZATION, getBasicAuthenticationHeader("username1", "password1"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.username").value("username1")) + .andExpect(jsonPath("$.accounts.length()").value(3)); + } + + @Test + @WithMockUser(roles = "ADMIN") + void createUser_Test_OK() throws Exception { + JSONObject userRequest = new JSONObject(); + userRequest.put("username", "username"); + userRequest.put("password", "password"); + + mockMvc.perform(post("/user") + .contentType(MediaType.APPLICATION_JSON) + .content(userRequest.toString())) + .andExpect(status().isOk()); + } + @Test + @WithMockUser(roles = "ADMIN") + void createUser_Test_TrowUserAlreadyExistsException() throws Exception { + JSONObject userRequest = new JSONObject(); + userRequest.put("username", "username1"); + userRequest.put("password", "password1"); + + mockMvc.perform(post("/user") + .contentType(MediaType.APPLICATION_JSON) + .content(userRequest.toString())) + .andExpect(status().is4xxClientError()); + } + + @Test + void depositToAccount_Test_OK() throws Exception { + JSONObject balanceChangeRequest = new JSONObject(); + balanceChangeRequest.put("amount", 10000L); + + mockMvc.perform(post("/account/deposit/{id}", getAccountIdByUsername("username1")) + .header(HttpHeaders.AUTHORIZATION, getBasicAuthenticationHeader("username1", "password1")) + .contentType(MediaType.APPLICATION_JSON) + .content(balanceChangeRequest.toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.amount").value(10001)); + + } + @Test + void withdrawFromAccount_Test_notOK_TrowInsufficientFundsException() throws Exception { + JSONObject balanceChangeRequest = new JSONObject(); + balanceChangeRequest.put("amount", 10000L); + + mockMvc.perform(post("/account/withdraw/{id}", 1) + .header(HttpHeaders.AUTHORIZATION, getBasicAuthenticationHeader("username1", "password1")) + .contentType(MediaType.APPLICATION_JSON) + .content(balanceChangeRequest.toString())) + .andExpect(status().is4xxClientError()); + + } + + + private User getUserByName(String username){ + return userRepository.findByUsername(username).orElseThrow(); + } + private long getUserIdByUserName(String username){ + return getUserByName(username).getId(); + } + private long getAccountIdByUsername(String username){ + long userId = getUserIdByUserName(username); + long ost = userId % userRepository.count(); + long count = (userId - ost) / userRepository.count(); + return count * 3 * userRepository.count() + ost*3; + } +} \ No newline at end of file diff --git a/src/test/java/resourses/application.properties b/src/test/java/resourses/application.properties new file mode 100644 index 0000000..e69de29