diff --git a/pom.xml b/pom.xml index 3d8377c..692b0e3 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,16 @@ postgresql test + + org.springframework.security + spring-security-test + test + + + com.github.javafaker + javafaker + 0.15 + diff --git a/src/test/java/com/skypro/simplebanking/SimpleBankingApplicationTests.java b/src/test/java/com/skypro/simplebanking/SimpleBankingApplicationTests.java index 1d4dd66..96072c1 100644 --- a/src/test/java/com/skypro/simplebanking/SimpleBankingApplicationTests.java +++ b/src/test/java/com/skypro/simplebanking/SimpleBankingApplicationTests.java @@ -6,4 +6,8 @@ @SpringBootTest class SimpleBankingApplicationTests { + @Test + void contextLoads() { + } + } diff --git a/src/test/java/com/skypro/simplebanking/TestData.java b/src/test/java/com/skypro/simplebanking/TestData.java new file mode 100644 index 0000000..9851e1d --- /dev/null +++ b/src/test/java/com/skypro/simplebanking/TestData.java @@ -0,0 +1,81 @@ +package com.skypro.simplebanking; + +import com.github.javafaker.Faker; +import com.skypro.simplebanking.dto.BankingUserDetails; +import com.skypro.simplebanking.dto.TransferRequest; +import com.skypro.simplebanking.entity.Account; +import com.skypro.simplebanking.entity.AccountCurrency; +import com.skypro.simplebanking.entity.User; + +import java.util.ArrayList; +import java.util.Random; + +public class TestData { + + + public static final Faker faker = new Faker(); + + public static Long getTestRandomLong() { + return new Random().nextLong(1000); + } + public static String getTestPassword() { + return faker.number().digits(10); + } + public static String getTestUserName() { + return faker.name().name(); + } + + public static User getTestUser() { + User user = new User(); + user.setId(getTestRandomLong()); + user.setUsername(getTestUserName()); + user.setPassword(getTestPassword()); + user.setAccounts(new ArrayList<>()); + + for (AccountCurrency currency : AccountCurrency.values()) { + Account account = new Account(); + account.setId(getTestRandomLong()); + account.setUser(user); + account.setAccountCurrency(currency); + account.setAmount(getTestRandomLong()); + user.getAccounts().add(account); + } + return user; + } + + public static BankingUserDetails getAdminBankingUserDetails(User user) { + return new BankingUserDetails(getTestRandomLong(), user.getUsername(), user.getPassword(), true); + } + public static long getUserAmount(User user, AccountCurrency accountCurrency) { + return user.getAccounts() + .stream() + .filter(x -> x.getAccountCurrency().equals(accountCurrency)) + .mapToLong(Account::getAmount) + .findAny() + .orElseThrow(); + } + public static TransferRequest getTestTransferRequest(User fromUser, AccountCurrency fromAccountCurrency, User toUser, AccountCurrency toAccountCurrency, long amount) { + TransferRequest transferRequest = new TransferRequest(); + transferRequest.setFromAccountId(fromUser.getAccounts() + .stream() + .filter(x -> x.getAccountCurrency().equals(fromAccountCurrency)) + .findFirst() + .orElseThrow() + .getId()); + transferRequest.setToUserId(toUser.getId()); + transferRequest.setToAccountId(toUser.getAccounts() + .stream() + .filter(x -> x.getAccountCurrency().equals(toAccountCurrency)) + .findFirst() + .orElseThrow() + .getId()); + transferRequest.setAmount(fromUser.getAccounts() + .stream() + .filter(x -> x.getAccountCurrency().equals(AccountCurrency.RUB)) + .findFirst() + .map(x -> x.getAmount() - 1) + .orElseThrow()); + transferRequest.setAmount(amount); + return transferRequest; + } +} diff --git a/src/test/java/com/skypro/simplebanking/controller/AccountControllerTests.java b/src/test/java/com/skypro/simplebanking/controller/AccountControllerTests.java new file mode 100644 index 0000000..8ef74ff --- /dev/null +++ b/src/test/java/com/skypro/simplebanking/controller/AccountControllerTests.java @@ -0,0 +1,223 @@ +package com.skypro.simplebanking.controller; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.skypro.simplebanking.SimpleBankingApplication; +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.UserRepository; +import com.skypro.simplebanking.repository.AccountRepository; +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.test.web.servlet.MockMvc; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; + +import static com.skypro.simplebanking.TestData.*; +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; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; + +@SpringBootTest(classes = SimpleBankingApplication.class) +@AutoConfigureMockMvc +@Transactional +public class AccountControllerTests { + + @Autowired + private MockMvc mockMvc; + @Autowired + private UserRepository userRepository; + @Autowired + private AccountRepository accountRepository; + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final BalanceChangeRequest balanceChangeRequest = new BalanceChangeRequest(); + + + @BeforeEach + void createBalanceChangeRequest(){ + balanceChangeRequest.setAmount(getTestRandomLong()); + } + + @AfterEach + void clearTestRepository() { + userRepository.deleteAll(); + accountRepository.deleteAll(); + } + + @Test + void getUserAccount_allDataCorrect_expectedCorrectAccount() throws Exception { + User user = userRepository.save(getTestUser()); + BankingUserDetails userDetails = BankingUserDetails.from(user); + + for (long id : user.getAccounts().stream().map(Account::getId).toList()) { + mockMvc.perform(get("/account/{id}", id) + .with(user(userDetails))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").exists()) + .andExpect(jsonPath("$.id").value(id)); + } + } + @Test + void getUserAccount_notCorrectRole_expected_exception() throws Exception { + User user = userRepository.save(getTestUser()); + BankingUserDetails userDetails = getAdminBankingUserDetails(user); + + for (long id : user.getAccounts().stream().map(Account::getId).toList()) { + mockMvc.perform(get("/account/{id}", id) + .with(user(userDetails))) + .andExpect(status().is4xxClientError()); + } + } + @Test + void getUserAccount_userDoesntExist_expected_exception() throws Exception { + User user = getTestUser(); + BankingUserDetails userDetails = BankingUserDetails.from(user); + + for (long id : user.getAccounts().stream().map(Account::getId).toList()) { + mockMvc.perform(get("/account/{id}", id) + .with(user(userDetails))) + .andExpect(status().is4xxClientError()); + } + } + + @Test + void depositToAccount_allDataCorrect_expected_ok() throws Exception { + + User user = userRepository.save(getTestUser()); + BankingUserDetails userDetails = BankingUserDetails.from(user); + + for (Account account : user.getAccounts()) { + mockMvc.perform(post("/account/deposit/{id}", account.getId()) + .with(user(userDetails)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(balanceChangeRequest))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").exists()) + .andExpect(jsonPath("$.id").value(account.getId())) + .andExpect(jsonPath("$.currency").value(account.getAccountCurrency().name())) + .andExpect(jsonPath("$.amount").value(user.getAccounts() + .stream() + .filter(x -> x.getId().equals(account.getId())) + .findFirst() + .orElseThrow() + .getAmount())); + } + } + @Test + void depositToAccount_notCorrectRole_expected_exception() throws Exception { + + User user = userRepository.save(getTestUser()); + BankingUserDetails userDetails = getAdminBankingUserDetails(user); + + for (Account account : user.getAccounts()) { + mockMvc.perform(post("/account/deposit/{id}", account.getId()) + .with(user(userDetails)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(balanceChangeRequest))) + .andExpect(status().is4xxClientError()); + } + } + @Test + void depositToAccount_userDoesntExist_expected_exception() throws Exception { + + User user = getTestUser(); + BankingUserDetails userDetails = BankingUserDetails.from(user); + + for (Account account : user.getAccounts()) { + mockMvc.perform(post("/account/deposit/{id}", account.getId()) + .with(user(userDetails)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(balanceChangeRequest))) + .andExpect(status().is4xxClientError()); + } + } + @Test + void depositToAccount_notCorrectAmount_expected_exception() throws Exception { + + balanceChangeRequest.setAmount(-100L); + + User user = userRepository.save(getTestUser()); + BankingUserDetails userDetails = BankingUserDetails.from(user); + + for (Account account : user.getAccounts()) { + mockMvc.perform(post("/account/deposit/{id}", account.getId()) + .with(user(userDetails)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(balanceChangeRequest))) + .andExpect(status().is4xxClientError()); + } + } + @Test + void withdrawFromAccount_allDataCorrect_expected_ok() throws Exception { + User user = userRepository.save(getTestUser()); + BankingUserDetails userDetails = BankingUserDetails.from(user); + balanceChangeRequest.setAmount(1); + + for (Account account : user.getAccounts()) { + mockMvc.perform(post("/account/withdraw/{id}", account.getId()) + .with(user(userDetails)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(balanceChangeRequest))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").exists()) + .andExpect(jsonPath("$.id").value(account.getId())) + .andExpect(jsonPath("$.currency").value(account.getAccountCurrency().name())) + .andExpect(jsonPath("$.amount").value(user.getAccounts() + .stream() + .filter(x -> x.getId().equals(account.getId())) + .findFirst() + .orElseThrow() + .getAmount())); + } + } + @Test + void withdrawFromAccount_notCorrectRole_expected_exception() throws Exception { + User user = userRepository.save(getTestUser()); + BankingUserDetails userDetails = getAdminBankingUserDetails(user); + balanceChangeRequest.setAmount(1); + + for (Account account : user.getAccounts()) { + mockMvc.perform(post("/account/withdraw/{id}", account.getId()) + .with(user(userDetails)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(balanceChangeRequest))) + .andExpect(status().is4xxClientError()); + } + } + @Test + void withdrawFromAccount_userDoesntExist_expected_exception() throws Exception { + User user = getTestUser(); + BankingUserDetails userDetails = BankingUserDetails.from(user); + balanceChangeRequest.setAmount(1); + + for (Account account : user.getAccounts()) { + mockMvc.perform(post("/account/withdraw/{id}", account.getId()) + .with(user(userDetails)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(balanceChangeRequest))) + .andExpect(status().is4xxClientError()); + } + } + @Test + void withdrawFromAccount_notCorrectAmount_expected_exception() throws Exception { + User user = userRepository.save(getTestUser()); + BankingUserDetails userDetails = BankingUserDetails.from(user); + balanceChangeRequest.setAmount(-100L); + + for (Account account : user.getAccounts()) { + mockMvc.perform(post("/account/withdraw/{id}", account.getId()) + .with(user(userDetails)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(balanceChangeRequest))) + .andExpect(status().is4xxClientError()); + } + } +} diff --git a/src/test/java/com/skypro/simplebanking/controller/TransferControllerTests.java b/src/test/java/com/skypro/simplebanking/controller/TransferControllerTests.java new file mode 100644 index 0000000..6bf7717 --- /dev/null +++ b/src/test/java/com/skypro/simplebanking/controller/TransferControllerTests.java @@ -0,0 +1,140 @@ +package com.skypro.simplebanking.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.skypro.simplebanking.SimpleBankingApplication; +import com.skypro.simplebanking.dto.BankingUserDetails; +import com.skypro.simplebanking.dto.TransferRequest; +import com.skypro.simplebanking.entity.AccountCurrency; +import com.skypro.simplebanking.entity.User; +import com.skypro.simplebanking.repository.UserRepository; +import com.skypro.simplebanking.repository.AccountRepository; +import org.junit.jupiter.api.AfterEach; +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.test.web.servlet.MockMvc; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; + +import static org.assertj.core.api.Assertions.assertThat; +import static com.skypro.simplebanking.TestData.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; + + +@SpringBootTest(classes = SimpleBankingApplication.class) +@AutoConfigureMockMvc +@Transactional +public class TransferControllerTests { + @Autowired + private MockMvc mockMvc; + @Autowired + private UserRepository userRepository; + @Autowired + private AccountRepository accountRepository; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + + @AfterEach + void clearTestRepository() { + userRepository.deleteAll(); + accountRepository.deleteAll(); + } + + @Test + void transfer_allDataCorrect_expected_ok() throws Exception { + + User fromUser = userRepository.save(getTestUser()); + BankingUserDetails userDetails = BankingUserDetails.from(fromUser); + User toUser = userRepository.save(getTestUser()); + long senderAmount = getUserAmount(fromUser, AccountCurrency.RUB); + long receiverAmount = getUserAmount(toUser, AccountCurrency.RUB); + TransferRequest transferRequest = getTestTransferRequest(fromUser, AccountCurrency.RUB, toUser, AccountCurrency.RUB, senderAmount); + + mockMvc.perform(post("/transfer/") + .with(user(userDetails)) + .content(objectMapper.writeValueAsString(transferRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + long senderActualAmount = getUserAmount(userRepository.findById(fromUser.getId()).orElseThrow(), AccountCurrency.RUB); + long receiverActualAmount = getUserAmount(userRepository.findById(toUser.getId()).orElseThrow(), AccountCurrency.RUB); + + assertThat(senderActualAmount).isEqualTo(0); + assertThat(receiverActualAmount).isEqualTo(receiverAmount + senderAmount); + + } + + @Test + void transfer_notCorrectRole_expected_exception() throws Exception { + + User fromUser = userRepository.save(getTestUser()); + BankingUserDetails userDetails = getAdminBankingUserDetails(fromUser); + User toUser = userRepository.save(getTestUser()); + long senderAmount = getUserAmount(fromUser, AccountCurrency.RUB); + TransferRequest transferRequest = getTestTransferRequest(fromUser, AccountCurrency.RUB, toUser, AccountCurrency.RUB, senderAmount); + + mockMvc.perform(post("/transfer/") + .with(user(userDetails)) + .content(objectMapper.writeValueAsString(transferRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is4xxClientError()); + + } + + @Test + void transfer_senderDoesntExist_expected_exception() throws Exception { + + User fromUser = getTestUser(); + BankingUserDetails userDetails = BankingUserDetails.from(fromUser); + User toUser = userRepository.save(getTestUser()); + long senderAmount = getUserAmount(fromUser, AccountCurrency.RUB); + TransferRequest transferRequest = getTestTransferRequest(fromUser, AccountCurrency.RUB, toUser, AccountCurrency.RUB, senderAmount); + + mockMvc.perform(post("/transfer/") + .with(user(userDetails)) + .content(objectMapper.writeValueAsString(transferRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is4xxClientError()); + + } + + @Test + void transfer_receiverDoesntExist_expected_exception() throws Exception { + + User fromUser = userRepository.save(getTestUser()); + BankingUserDetails userDetails = BankingUserDetails.from(fromUser); + User toUser = getTestUser(); + long senderAmount = getUserAmount(fromUser, AccountCurrency.RUB); + TransferRequest transferRequest = getTestTransferRequest(fromUser, AccountCurrency.RUB, toUser, AccountCurrency.RUB, senderAmount); + + mockMvc.perform(post("/transfer/") + .with(user(userDetails)) + .content(objectMapper.writeValueAsString(transferRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is4xxClientError()); + + } + + @Test + void transfer_notCorrectAmount_expected_ok() throws Exception { + + User fromUser = userRepository.save(getTestUser()); + BankingUserDetails userDetails = BankingUserDetails.from(fromUser); + User toUser = userRepository.save(getTestUser()); + long senderAmount = -100L; + + TransferRequest transferRequest = getTestTransferRequest(fromUser, AccountCurrency.RUB, toUser, AccountCurrency.RUB, senderAmount); + + mockMvc.perform(post("/transfer/") + .with(user(userDetails)) + .content(objectMapper.writeValueAsString(transferRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is4xxClientError()); + + + } +} diff --git a/src/test/java/com/skypro/simplebanking/controller/UserControllerTests.java b/src/test/java/com/skypro/simplebanking/controller/UserControllerTests.java new file mode 100644 index 0000000..c390766 --- /dev/null +++ b/src/test/java/com/skypro/simplebanking/controller/UserControllerTests.java @@ -0,0 +1,151 @@ +package com.skypro.simplebanking.controller; + +import com.skypro.simplebanking.SimpleBankingApplication; +import com.skypro.simplebanking.dto.BankingUserDetails; +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.MediaType; +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.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; + + +import static com.skypro.simplebanking.TestData.*; + + +@SpringBootTest(classes = SimpleBankingApplication.class) +@AutoConfigureMockMvc +public class UserControllerTests { + @Autowired + MockMvc mockMvc; + @Autowired + private UserRepository userRepository; + @Autowired + private AccountRepository accountRepository; + @Autowired + private UserService userService; + + @BeforeEach + void createTestRepository() { + userService.createUser("Sergey", "123"); + } + + @AfterEach + void clearTestRepository() { + userRepository.deleteAll(); + accountRepository.deleteAll(); + } + + + @Test + @WithMockUser(roles = "ADMIN") + void createUser_expected_OK() throws Exception { + JSONObject userRequest = new JSONObject(); + userRequest.put("username", "Ivan"); + userRequest.put("password", "123"); + + mockMvc.perform(post("/user/") + .contentType(MediaType.APPLICATION_JSON) + .content(userRequest.toString())) + .andExpect(status().isOk()); + + } + @Test + @WithMockUser(roles = "USER") + void createUserWithNotCorrectRole_expected_exception() throws Exception { + JSONObject userRequest = new JSONObject(); + userRequest.put("username", "Ivan"); + userRequest.put("password", "1234"); + + mockMvc.perform(post("/user/") + .contentType(MediaType.APPLICATION_JSON) + .content(userRequest.toString())) + .andExpect(status().is4xxClientError()); + + } + + + + @Test + @WithMockUser(roles = "ADMIN") + void createUser_expected_userAlreadyExistException() throws Exception { + + JSONObject userRequest = new JSONObject(); + userRequest.put("username", "Sergey"); + userRequest.put("password", "123"); + + mockMvc.perform(post("/user/") + .contentType(MediaType.APPLICATION_JSON) + .content(userRequest.toString())) + .andExpect(status().is4xxClientError()); + + } + + @Test + @WithMockUser(roles = "USER") + void getAllUsers_expectedNonEmptyArray() throws Exception { + + mockMvc.perform(get("/user/list")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").isNotEmpty()) + .andExpect(jsonPath("$.length()").value(1)); + + } + + @Test + @WithMockUser(roles = "USER") + void getAllUsers_expectedEmptyArray() throws Exception { + clearTestRepository(); + mockMvc.perform(get("/user/list")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").isEmpty()); + } + + @Test + @WithMockUser(roles = "ADMIN") + void getAllUsers_WithAdminRole_expected_exception() throws Exception { + mockMvc.perform(get("/user/list")) + .andExpect(status().is4xxClientError()); + } + + @Test + void getMyProfile_withCorrectRole_expected_ok() throws Exception { + User user = userRepository.save(getTestUser()); + BankingUserDetails bankingUserDetails = BankingUserDetails.from(user); + + mockMvc.perform(get("/user/me") + .with(user(bankingUserDetails))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(user.getId())) + .andExpect(jsonPath("$.username").value(user.getUsername())) + .andExpect(jsonPath("$.accounts").isArray()) + .andExpect(jsonPath("$.accounts").isNotEmpty()); + } + @Test + void getMyProfile_incorrectRole_expected_exception() throws Exception { + User user = userRepository.save(getTestUser()); + BankingUserDetails bankingUserDetails = getAdminBankingUserDetails(user); + + mockMvc.perform(get("/user/me") + .with(user(bankingUserDetails))) + .andExpect(status().is4xxClientError()); + } +} + diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..4eaeb1c --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,6 @@ +spring.datasource.url=jdbc:tc:postgresql://localhost:5432/banking +spring.datasource.username=banking +spring.datasource.password=super-safe-pass +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.open-in-view=false +app.security.admin-token=SUPER_SECRET_KEY_FROM_ADMIN