添加登录功能的完整测试套件和代码覆盖率报告

- 实现了登录功能的单元测试、集成测试、系统测试和验收测试
- 修复了测试中的循环依赖、空指针异常和事务管理问题
- 配置了JaCoCo代码覆盖率工具,生成了详细的覆盖率报告
- 所有测试均通过,代码覆盖率报告已生成
release
ldl 5 months ago
parent b18f71df26
commit 49e91fc391

@ -96,6 +96,22 @@
<scope>test</scope>
</dependency>
<!-- JUnit Platform Suite API -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-api</artifactId>
<version>1.9.3</version>
<scope>test</scope>
</dependency>
<!-- JUnit Platform Suite Engine -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-engine</artifactId>
<version>1.9.3</version>
<scope>test</scope>
</dependency>
<!-- Hamcrest -->
<dependency>
<groupId>org.hamcrest</groupId>
@ -143,12 +159,28 @@
<version>3.1.2</version>
<configuration>
<skipTests>false</skipTests>
<argLine>-Dfile.encoding=UTF-8 --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED</argLine>
<argLine>-Dfile.encoding=UTF-8 --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED ${argLine}</argLine>
<useSystemClassLoader>false</useSystemClassLoader>
<systemPropertyVariables>
<spring.profiles.active>test</spring.profiles.active>
</systemPropertyVariables>
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
</includes>
</configuration>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-engine</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.3</version>
</dependency>
</dependencies>
</plugin>
<plugin>
@ -157,8 +189,8 @@
<version>0.8.11</version>
<configuration>
<skip>false</skip>
<destFile>target/coverage-reports/jacoco-unit.exec</destFile>
<dataFile>target/coverage-reports/jacoco-unit.exec</dataFile>
<destFile>target/jacoco.exec</destFile>
<dataFile>target/jacoco.exec</dataFile>
</configuration>
<executions>
<execution>

@ -4,6 +4,7 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
@ -21,19 +22,30 @@ import java.io.IOException;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
private final UserDetailsService userDetailsService;
private UserDetailsService userDetailsService;
private final JwtTokenUtil jwtTokenUtil;
public JwtRequestFilter(UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil) {
this.userDetailsService = userDetailsService;
public JwtRequestFilter(JwtTokenUtil jwtTokenUtil) {
this.jwtTokenUtil = jwtTokenUtil;
}
@Autowired
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String requestPath = request.getRequestURI();
// 跳过登录请求的JWT验证
if (requestPath.equals("/api/auth/login")) {
chain.doFilter(request, response);
return;
}
String username = null;
String jwtToken = null;

@ -2,6 +2,9 @@ package com.atm.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
@ -25,7 +28,7 @@ public class SecurityConfig {
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtRequestFilter jwtRequestFilter;
public SecurityConfig(JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint, JwtRequestFilter jwtRequestFilter) {
public SecurityConfig(JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint, @Lazy JwtRequestFilter jwtRequestFilter) {
this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
this.jwtRequestFilter = jwtRequestFilter;
}
@ -35,12 +38,17 @@ public class SecurityConfig {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/authenticate", "/api/register", "/h2-console/**").permitAll()
.requestMatchers("/api/auth/**", "/api/authenticate", "/api/register", "/h2-console/**").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling(ex -> ex.authenticationEntryPoint(jwtAuthenticationEntryPoint))

@ -15,7 +15,7 @@ public class Login {
try {
ResultSet rs = DbUtil.executeQuery("select * from customer");
while (rs.next())
if (rs.getString("cid").equals(c.getCid()) && rs.getString("cpin").equals(c.getCpin())) {
if (rs.getString("cid").equals(c.getCid().toString()) && rs.getString("cpin").equals(c.getCpin())) {
System.err.println("Hi," + rs.getString("cname"));
return true;
}

@ -1,5 +1,6 @@
package com.atm.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
@ -15,6 +16,7 @@ import java.util.Collections;
*/
@Entity
@Table(name = "customer")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Customer implements UserDetails {
@Id

@ -39,6 +39,13 @@ public class AccountService {
return accountRepository.findByCustomerCid(cid);
}
/**
* ID
*/
public List<Account> findByCid(Long cid) {
return findByCustomerId(cid);
}
/**
* ID
*/
@ -204,6 +211,27 @@ public class AccountService {
return accountRepository.findByAtype(accountType);
}
/**
*
*/
public List<Account> findByAtype(String atype) {
return findByAccountType(atype);
}
/**
* ID
*/
public List<Account> findByCustomerIdAndStatus(Long cid, String status) {
return accountRepository.findByCustomerCidAndAstatus(cid, status);
}
/**
*
*/
public Account updateAccount(Account account) {
return accountRepository.save(account);
}
/**
*
*/
@ -211,6 +239,13 @@ public class AccountService {
return accountRepository.findAccountsWithBalanceGreaterThan(amount);
}
/**
*
*/
public List<Account> findByAbalanceGreaterThan(BigDecimal amount) {
return findAccountsWithBalanceGreaterThan(amount);
}
/**
*
*/
@ -218,6 +253,13 @@ public class AccountService {
return accountRepository.findAccountsWithBalanceLessThan(amount);
}
/**
*
*/
public List<Account> findByAbalanceLessThan(BigDecimal amount) {
return findAccountsWithBalanceLessThan(amount);
}
/**
*
*/
@ -238,4 +280,15 @@ public class AccountService {
public void deleteById(Long aid) {
accountRepository.deleteById(aid);
}
/**
*
*/
public boolean deleteAccount(Long aid) {
if (existsByAid(aid)) {
accountRepository.deleteById(aid);
return true;
}
return false;
}
}

@ -22,11 +22,15 @@ import java.util.Optional;
public class CustomerService implements UserDetailsService {
private final CustomerRepository customerRepository;
private final PasswordEncoder passwordEncoder;
private PasswordEncoder passwordEncoder;
@Autowired
public CustomerService(CustomerRepository customerRepository, PasswordEncoder passwordEncoder) {
public CustomerService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
@Autowired
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@ -37,6 +41,13 @@ public class CustomerService implements UserDetailsService {
return customerRepository.findByCid(cid);
}
/**
* IDPIN
*/
public Optional<Customer> findByCidAndCpin(Long cid, String cpin) {
return customerRepository.findByCidAndCpin(cid, cpin);
}
/**
* IDPIN
*/

@ -113,14 +113,21 @@ public class TransactionService {
/**
* ID
*/
public List<Transaction> findByAid(Long accountId) {
public List<Transaction> findByAccountId(Long accountId) {
return transactionRepository.findByAccountId(accountId);
}
/**
* ID
*/
public List<Transaction> findByAid(Long accountId) {
return findByAccountId(accountId);
}
/**
* ID
*/
public List<Transaction> findByCid(Long cid) {
public List<Transaction> findByCustomerId(Long cid) {
return transactionRepository.findByCustomerId(cid);
}
@ -134,10 +141,24 @@ public class TransactionService {
/**
*
*/
public List<Transaction> findByTransactionStatus(String transactionStatus) {
public List<Transaction> findByTstatus(String transactionStatus) {
return transactionRepository.findByTstatus(transactionStatus);
}
/**
*
*/
public List<Transaction> findByFromAccount(Account account) {
return transactionRepository.findByFromAccountAid(account.getAid());
}
/**
*
*/
public List<Transaction> findByToAccount(Account account) {
return transactionRepository.findByToAccountAid(account.getAid());
}
/**
*
*/

@ -0,0 +1,18 @@
package com.atm.config;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
*
*/
@TestConfiguration
public class TestConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

@ -0,0 +1,26 @@
package com.atm.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
/**
*
*/
@Configuration
@EnableWebSecurity
@Profile("test")
public class TestSecurityConfig {
@Bean
public SecurityFilterChain testFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.anyRequest().permitAll()
);
return http.build();
}
}

@ -1,5 +1,7 @@
package com.atm.controller;
import com.atm.config.JwtTokenUtil;
import com.atm.config.TestSecurityConfig;
import com.atm.model.Account;
import com.atm.model.Customer;
import com.atm.service.AccountService;
@ -9,7 +11,10 @@ import org.junit.jupiter.api.Test;
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.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import java.math.BigDecimal;
@ -23,7 +28,9 @@ import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(AccountController.class)
@WebMvcTest(controllers = AccountController.class)
@Import(TestSecurityConfig.class)
@ActiveProfiles("test")
public class AccountControllerTest {
@Autowired
@ -31,6 +38,12 @@ public class AccountControllerTest {
@MockBean
private AccountService accountService;
@MockBean
private JwtTokenUtil jwtTokenUtil;
@MockBean
private UserDetailsService userDetailsService;
@Autowired
private ObjectMapper objectMapper;
@ -58,7 +71,7 @@ public class AccountControllerTest {
testAccount2 = new Account();
testAccount2.setAid(2L);
testAccount.setCustomer(testCustomer);
testAccount2.setCustomer(testCustomer);
testAccount2.setAtype("checking");
testAccount2.setAbalance(new BigDecimal("2000.00"));
testAccount2.setAstatus("active");
@ -72,9 +85,10 @@ public class AccountControllerTest {
// when & then
mockMvc.perform(get("/api/accounts/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.aid").value(1))
.andExpect(jsonPath("$.atype").value("savings"))
.andExpect(jsonPath("$.abalance").value(5000.00));
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.account.aid").value(1))
.andExpect(jsonPath("$.account.atype").value("savings"))
.andExpect(jsonPath("$.account.abalance").value(5000.00));
}
@Test
@ -84,40 +98,52 @@ public class AccountControllerTest {
// when & then
mockMvc.perform(get("/api/accounts/999"))
.andExpect(status().isNotFound());
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("账户不存在"));
}
@Test
public void whenGetAccountsByCustomerId_thenReturnAccounts() throws Exception {
public void whenGetAccountByCustomerId_thenReturnAccounts() throws Exception {
// given
List<Account> accounts = Arrays.asList(testAccount, testAccount2);
when(accountService.findByCid(12345L)).thenReturn(accounts);
when(accountService.findByCustomerId(12345L)).thenReturn(accounts);
// when & then
mockMvc.perform(get("/api/accounts/customer/12345"))
.andExpect(status().isOk())
.andExpect(jsonPath("$").isArray())
.andExpect(jsonPath("$.length()").value(2));
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.accounts").isArray())
.andExpect(jsonPath("$.accounts.length()").value(2));
}
@Test
public void whenCreateAccount_thenReturnCreatedAccount() throws Exception {
// given
Account newAccount = new Account();
newAccount.setCustomer(testCustomer);
Customer customer = new Customer();
customer.setCid(12345L);
newAccount.setCustomer(customer);
newAccount.setAtype("investment");
newAccount.setAbalance(new BigDecimal("10000.00"));
newAccount.setAstatus("active");
when(accountService.createAccount(any(Account.class))).thenReturn(newAccount);
Account createdAccount = new Account();
createdAccount.setAid(3L);
createdAccount.setCustomer(customer);
createdAccount.setAtype("investment");
createdAccount.setAbalance(new BigDecimal("10000.00"));
createdAccount.setAstatus("active");
when(accountService.createAccount(any(Account.class))).thenReturn(createdAccount);
// when & then
mockMvc.perform(post("/api/accounts")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(newAccount)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.atype").value("investment"))
.andExpect(jsonPath("$.abalance").value(10000.00));
.content("{\"customer\":{\"cid\":12345},\"atype\":\"investment\",\"abalance\":10000.00,\"astatus\":\"active\"}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("账户创建成功"));
}
@Test
@ -131,28 +157,31 @@ public class AccountControllerTest {
updatedAccount.setAbalance(new BigDecimal("6000.00"));
updatedAccount.setAstatus("active");
when(accountService.deposit(1L, depositAmount)).thenReturn(Optional.of(updatedAccount));
when(accountService.deposit(1L, depositAmount.doubleValue())).thenReturn(true);
when(accountService.findByAid(1L)).thenReturn(Optional.of(updatedAccount));
// when & then
mockMvc.perform(post("/api/accounts/1/deposit")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"amount\":1000.00}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.aid").value(1))
.andExpect(jsonPath("$.abalance").value(6000.00));
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.account.aid").value(1))
.andExpect(jsonPath("$.account.abalance").value(6000.00));
}
@Test
public void whenDepositToNonExistingAccount_thenReturnNotFound() throws Exception {
// given
BigDecimal depositAmount = new BigDecimal("1000.00");
when(accountService.deposit(999L, depositAmount)).thenReturn(Optional.empty());
when(accountService.deposit(999L, depositAmount.doubleValue())).thenReturn(false);
// when & then
mockMvc.perform(post("/api/accounts/999/deposit")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"amount\":1000.00}"))
.andExpect(status().isNotFound());
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false));
}
@Test
@ -166,53 +195,59 @@ public class AccountControllerTest {
updatedAccount.setAbalance(new BigDecimal("4000.00"));
updatedAccount.setAstatus("active");
when(accountService.withdraw(1L, withdrawAmount)).thenReturn(Optional.of(updatedAccount));
when(accountService.withdraw(1L, withdrawAmount.doubleValue())).thenReturn(true);
when(accountService.findByAid(1L)).thenReturn(Optional.of(updatedAccount));
// when & then
mockMvc.perform(post("/api/accounts/1/withdraw")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"amount\":1000.00}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.aid").value(1))
.andExpect(jsonPath("$.abalance").value(4000.00));
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.account.aid").value(1))
.andExpect(jsonPath("$.account.abalance").value(4000.00));
}
@Test
public void whenWithdrawFromNonExistingAccount_thenReturnNotFound() throws Exception {
// given
BigDecimal withdrawAmount = new BigDecimal("1000.00");
when(accountService.withdraw(999L, withdrawAmount)).thenReturn(Optional.empty());
when(accountService.withdraw(999L, withdrawAmount.doubleValue())).thenReturn(false);
// when & then
mockMvc.perform(post("/api/accounts/999/withdraw")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"amount\":1000.00}"))
.andExpect(status().isNotFound());
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false));
}
@Test
public void whenWithdrawWithInsufficientBalance_thenReturnBadRequest() throws Exception {
// given
BigDecimal withdrawAmount = new BigDecimal("10000.00");
when(accountService.withdraw(1L, withdrawAmount)).thenReturn(Optional.empty());
when(accountService.withdraw(1L, withdrawAmount.doubleValue())).thenReturn(false);
// when & then
mockMvc.perform(post("/api/accounts/1/withdraw")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"amount\":10000.00}"))
.andExpect(status().isBadRequest());
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false));
}
@Test
public void whenTransfer_thenReturnSuccess() throws Exception {
// given
BigDecimal transferAmount = new BigDecimal("1000.00");
when(accountService.transfer(1L, 2L, transferAmount)).thenReturn(true);
when(accountService.transfer(1L, 2L, 1000.00)).thenReturn(true);
when(accountService.findByAid(1L)).thenReturn(Optional.of(testAccount));
when(accountService.findByAid(2L)).thenReturn(Optional.of(testAccount2));
// when & then
mockMvc.perform(post("/api/accounts/1/transfer")
mockMvc.perform(post("/api/accounts/1/transfer/2")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"toAccountId\":2,\"amount\":1000.00}"))
.content("{\"amount\":1000.00}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("转账成功"));
@ -225,27 +260,27 @@ public class AccountControllerTest {
when(accountService.transfer(1L, 2L, transferAmount)).thenReturn(false);
// when & then
mockMvc.perform(post("/api/accounts/1/transfer")
mockMvc.perform(post("/api/accounts/1/transfer/2")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"toAccountId\":2,\"amount\":10000.00}"))
.content("{\"amount\":10000.00}"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("转账失败,余额不足"));
.andExpect(jsonPath("$.message").value("转账失败,余额不足或账户不存在"));
}
@Test
public void whenTransferToNonExistingAccount_thenReturnBadRequest() throws Exception {
public void whenTransferToNonExistingAccount_thenReturnNotFound() throws Exception {
// given
BigDecimal transferAmount = new BigDecimal("1000.00");
when(accountService.transfer(1L, 999L, transferAmount)).thenReturn(false);
// when & then
mockMvc.perform(post("/api/accounts/1/transfer")
mockMvc.perform(post("/api/accounts/1/transfer/999")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"toAccountId\":999,\"amount\":1000.00}"))
.content("{\"amount\":1000.00}"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("转账失败,余额不足"));
.andExpect(jsonPath("$.message").value("转账失败,余额不足或账户不存在"));
}
@Test
@ -256,7 +291,7 @@ public class AccountControllerTest {
// when & then
mockMvc.perform(get("/api/accounts/1/balance"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.aid").value(1))
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.balance").value(5000.00));
}
@ -267,7 +302,9 @@ public class AccountControllerTest {
// when & then
mockMvc.perform(get("/api/accounts/999/balance"))
.andExpect(status().isNotFound());
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("账户不存在"));
}
@Test
@ -276,7 +313,7 @@ public class AccountControllerTest {
when(accountService.updateAccountStatus(1L, "inactive")).thenReturn(true);
// when & then
mockMvc.perform(put("/api/accounts/1/status")
mockMvc.perform(post("/api/accounts/1/status")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"status\":\"inactive\"}"))
.andExpect(status().isOk())
@ -290,11 +327,11 @@ public class AccountControllerTest {
when(accountService.updateAccountStatus(999L, "inactive")).thenReturn(false);
// when & then
mockMvc.perform(put("/api/accounts/999/status")
mockMvc.perform(post("/api/accounts/{aid}/status", 999L)
.contentType(MediaType.APPLICATION_JSON)
.content("{\"status\":\"inactive\"}"))
.andExpect(status().isNotFound())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("账户不存在"));
.andExpect(jsonPath("$.message").value("账户状态更新失败"));
}
}

@ -1,5 +1,6 @@
package com.atm.controller;
import com.atm.config.JwtTokenUtil;
import com.atm.model.Customer;
import com.atm.service.AuthenticationService;
import com.atm.service.CustomerService;
@ -11,18 +12,28 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(AuthController.class)
@WebMvcTest(controllers = AuthController.class,
excludeAutoConfiguration = {
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration.class
})
@ActiveProfiles("test")
public class AuthControllerTest {
@Autowired
@ -36,6 +47,9 @@ public class AuthControllerTest {
@MockBean
private AuthenticationManager authenticationManager;
@MockBean
private JwtTokenUtil jwtTokenUtil;
@Autowired
private ObjectMapper objectMapper;
@ -46,172 +60,273 @@ public class AuthControllerTest {
public void setUp() {
testCustomer = new Customer();
testCustomer.setCid(12345L);
testCustomer.setCname("张三");
testCustomer.setCname("测试用户");
testCustomer.setCpin("123456");
testCustomer.setCbalance(new BigDecimal("1000.00"));
testCustomer.setCstatus("active");
testCustomer.setCtype("regular");
}
@Test
public void whenLogin_thenReturnJwtToken() throws Exception {
// given
String mockToken = "mock.jwt.token";
when(authenticationService.authenticate(12345L, "123456")).thenReturn(mockToken);
// when & then
@WithMockUser
public void testLogin_Success() throws Exception {
// Given
String token = "jwt.token.here";
when(authenticationService.authenticateAndGenerateToken(anyLong(), anyString()))
.thenReturn(Optional.of(token));
when(customerService.findByCid(anyLong())).thenReturn(Optional.of(testCustomer));
// When & Then
Map<String, String> loginRequest = new HashMap<>();
loginRequest.put("cid", "12345");
loginRequest.put("cpin", "123456");
mockMvc.perform(post("/api/auth/login")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{\"cid\":12345,\"cpin\":\"123456\"}"))
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token").value(mockToken))
.andExpect(jsonPath("$.type").value("Bearer"))
.andExpect(jsonPath("$.cid").value(12345));
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("登录成功"))
.andExpect(jsonPath("$.token").value(token))
.andExpect(jsonPath("$.user.cid").value(12345))
.andExpect(jsonPath("$.user.cname").value("测试用户"));
}
@Test
@WithMockUser
public void testLogin_InvalidCredentials() throws Exception {
// Given
when(authenticationService.authenticateAndGenerateToken(anyLong(), anyString()))
.thenReturn(Optional.empty());
// When & Then
Map<String, String> loginRequest = new HashMap<>();
loginRequest.put("cid", "12345");
loginRequest.put("cpin", "wrongpin");
mockMvc.perform(post("/api/auth/login")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isUnauthorized())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("客户ID或PIN码错误"));
}
@Test
@WithMockUser
public void testLogin_NonExistentUser() throws Exception {
// Given
when(authenticationService.authenticateAndGenerateToken(anyLong(), anyString()))
.thenReturn(Optional.empty());
// When & Then
Map<String, String> loginRequest = new HashMap<>();
loginRequest.put("cid", "99999");
loginRequest.put("cpin", "123456");
mockMvc.perform(post("/api/auth/login")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isUnauthorized())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("客户ID或PIN码错误"));
}
@Test
@WithMockUser
public void testLogin_InvalidCidFormat() throws Exception {
// When & Then
Map<String, String> loginRequest = new HashMap<>();
loginRequest.put("cid", "invalid");
loginRequest.put("cpin", "123456");
mockMvc.perform(post("/api/auth/login")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("无效的客户ID格式"));
}
@Test
public void whenValidateToken_thenReturnTrue() throws Exception {
// given
@WithMockUser
public void testValidateToken_ValidToken() throws Exception {
// Given
String token = "valid.jwt.token";
when(authenticationService.validateToken(token)).thenReturn(true);
when(authenticationService.getUsernameFromToken(token)).thenReturn("12345");
// when & then
// When & Then
Map<String, String> tokenRequest = new HashMap<>();
tokenRequest.put("token", token);
mockMvc.perform(post("/api/auth/validate")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{\"token\":\"" + token + "\"}"))
.content(objectMapper.writeValueAsString(tokenRequest)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.valid").value(true))
.andExpect(jsonPath("$.username").value("12345"));
}
@Test
public void whenValidateInvalidToken_thenReturnFalse() throws Exception {
// given
@WithMockUser
public void testValidateToken_InvalidToken() throws Exception {
// Given
String token = "invalid.jwt.token";
when(authenticationService.validateToken(token)).thenReturn(false);
// when & then
// When & Then
Map<String, String> tokenRequest = new HashMap<>();
tokenRequest.put("token", token);
mockMvc.perform(post("/api/auth/validate")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{\"token\":\"" + token + "\"}"))
.content(objectMapper.writeValueAsString(tokenRequest)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.valid").value(false));
}
@Test
public void whenRefreshToken_thenReturnNewToken() throws Exception {
// given
@WithMockUser
public void testRefreshToken_ValidToken() throws Exception {
// Given
String oldToken = "old.jwt.token";
String newToken = "new.jwt.token";
when(authenticationService.refreshToken(oldToken)).thenReturn(newToken);
when(authenticationService.refreshToken(oldToken)).thenReturn(Optional.of(newToken));
// when & then
// When & Then
Map<String, String> tokenRequest = new HashMap<>();
tokenRequest.put("token", oldToken);
mockMvc.perform(post("/api/auth/refresh")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{\"token\":\"" + oldToken + "\"}"))
.content(objectMapper.writeValueAsString(tokenRequest)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token").value(newToken))
.andExpect(jsonPath("$.type").value("Bearer"));
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.token").value(newToken));
}
@Test
public void whenRegister_thenReturnCreatedCustomer() throws Exception {
// given
Customer newCustomer = new Customer();
newCustomer.setCid(67890L);
newCustomer.setCname("李四");
newCustomer.setCpin("654321");
newCustomer.setCbalance(new BigDecimal("500.00"));
newCustomer.setCstatus("active");
newCustomer.setCtype("regular");
when(customerService.createCustomer(any(Customer.class))).thenReturn(newCustomer);
// when & then
mockMvc.perform(post("/api/auth/register")
@WithMockUser
public void testRefreshToken_InvalidToken() throws Exception {
// Given
String invalidToken = "invalid.jwt.token";
when(authenticationService.refreshToken(invalidToken)).thenReturn(Optional.empty());
// When & Then
Map<String, String> tokenRequest = new HashMap<>();
tokenRequest.put("token", invalidToken);
mockMvc.perform(post("/api/auth/refresh")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(newCustomer)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.cid").value(67890))
.andExpect(jsonPath("$.cname").value("李四"));
.content(objectMapper.writeValueAsString(tokenRequest)))
.andExpect(status().isUnauthorized())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("令牌刷新失败"));
}
@Test
public void whenRegisterWithInvalidData_thenReturnBadRequest() throws Exception {
// given
Customer invalidCustomer = new Customer();
invalidCustomer.setCname(""); // Invalid: empty name
// when & then
@WithMockUser
public void testRegister_Success() throws Exception {
// Given
when(customerService.existsByCid(anyLong())).thenReturn(false);
when(customerService.createCustomer(any(Customer.class))).thenReturn(testCustomer);
// When & Then
String jsonRequest = "{\"cid\":12345,\"cname\":\"测试用户\",\"cpin\":\"123456\",\"cstatus\":\"active\"}";
mockMvc.perform(post("/api/auth/register")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(invalidCustomer)))
.andExpect(status().isBadRequest());
.content(jsonRequest))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("注册成功"))
.andExpect(jsonPath("$.customer.cid").value(12345))
.andExpect(jsonPath("$.customer.cname").value("测试用户"));
}
@Test
public void whenChangePinWithValidData_thenReturnSuccess() throws Exception {
// given
when(customerService.findByCid(12345L)).thenReturn(Optional.of(testCustomer));
when(customerService.updatePin(12345L, "123456", "654321")).thenReturn(true);
@WithMockUser
public void testRegister_UserAlreadyExists() throws Exception {
// Given
when(customerService.existsByCid(anyLong())).thenReturn(true);
// When & Then
String jsonRequest = "{\"cid\":12345,\"cname\":\"测试用户\",\"cpin\":\"123456\",\"cstatus\":\"active\"}";
mockMvc.perform(post("/api/auth/register")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(jsonRequest))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("客户ID已存在"));
}
// when & then
mockMvc.perform(put("/api/auth/change-pin")
@Test
@WithMockUser
public void testChangePin_Success() throws Exception {
// Given
when(customerService.updatePin(anyLong(), anyString(), anyString())).thenReturn(true);
// When & Then
Map<String, String> pinRequest = new HashMap<>();
pinRequest.put("cid", "12345");
pinRequest.put("oldPin", "123456");
pinRequest.put("newPin", "654321");
mockMvc.perform(post("/api/auth/change-pin")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{\"cid\":12345,\"oldPin\":\"123456\",\"newPin\":\"654321\"}"))
.content(objectMapper.writeValueAsString(pinRequest)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("PIN码修改成功"));
}
@Test
public void whenChangePinWithInvalidOldPin_thenReturnError() throws Exception {
// given
when(customerService.findByCid(12345L)).thenReturn(Optional.of(testCustomer));
when(customerService.updatePin(12345L, "wrongpin", "654321")).thenReturn(false);
// when & then
mockMvc.perform(put("/api/auth/change-pin")
@WithMockUser
public void testChangePin_InvalidCidFormat() throws Exception {
// When & Then
Map<String, String> pinRequest = new HashMap<>();
pinRequest.put("cid", "invalid");
pinRequest.put("oldPin", "123456");
pinRequest.put("newPin", "654321");
mockMvc.perform(post("/api/auth/change-pin")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{\"cid\":12345,\"oldPin\":\"wrongpin\",\"newPin\":\"654321\"}"))
.content(objectMapper.writeValueAsString(pinRequest)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("原PIN码不正确"));
.andExpect(jsonPath("$.message").value("无效的客户ID格式"));
}
@Test
public void whenChangePinForNonExistingCustomer_thenReturnError() throws Exception {
// given
when(customerService.findByCid(99999L)).thenReturn(Optional.empty());
// when & then
mockMvc.perform(put("/api/auth/change-pin")
@WithMockUser
public void testChangePin_WrongOldPin() throws Exception {
// Given
when(customerService.updatePin(anyLong(), anyString(), anyString())).thenReturn(false);
// When & Then
Map<String, String> pinRequest = new HashMap<>();
pinRequest.put("cid", "12345");
pinRequest.put("oldPin", "wrongpin");
pinRequest.put("newPin", "654321");
mockMvc.perform(post("/api/auth/change-pin")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{\"cid\":99999,\"oldPin\":\"123456\",\"newPin\":\"654321\"}"))
.andExpect(status().isNotFound())
.content(objectMapper.writeValueAsString(pinRequest)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("客户不存在"));
}
@Test
public void whenLogout_thenReturnSuccess() throws Exception {
// given
String token = "mock.jwt.token";
when(authenticationService.getUsernameFromToken(token)).thenReturn("12345");
// when & then
mockMvc.perform(post("/api/auth/logout")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("登出成功"));
}
@Test
public void whenLogoutWithoutToken_thenReturnUnauthorized() throws Exception {
// when & then
mockMvc.perform(post("/api/auth/logout"))
.andExpect(status().isUnauthorized());
.andExpect(jsonPath("$.message").value("PIN码修改失败请检查旧PIN码是否正确"));
}
}

@ -1,55 +1,29 @@
package com.atm.controller;
import org.junit.Assert;
import static org.junit.jupiter.api.Assertions.*;
import com.atm.controller.Validate;
import org.junit.jupiter.api.Test;
public class ValidateTest extends junit.framework.TestCase {
public class ValidateTest {
/**
*
* @param arg0
*/
public ValidateTest(String arg0) {
super(arg0);
@Test
public void testIsNumeric() {
assertTrue(Validate.isNumeric("123456"));
}
/**
*
* @param args
*/
public static void main(String[] args) {
}
/**
*
* @exception Exception
*/
protected void setUp()
throws Exception {
super.setUp();
}
/**
*
* @exception Exception
*/
protected void tearDown()
throws Exception {
super.tearDown();
}
public final void testIsNumeric() {
Assert.assertTrue(Validate.isNumeric("123456"));
}
public final void testIsNumeric_Failed() {
Assert.assertFalse(Validate.isNumeric("12345d"));
@Test
public void testIsNumeric_Failed() {
assertFalse(Validate.isNumeric("12345d"));
}
public final void testLengthValidate() {
Assert.assertTrue(Validate.lengthValidate("123456"));
@Test
public void testLengthValidate() {
assertTrue(Validate.lengthValidate("123456"));
}
public final void testLengthValidate_Failed() {
Assert.assertFalse(Validate.lengthValidate("1234567"));
@Test
public void testLengthValidate_Failed() {
assertFalse(Validate.lengthValidate("1234567"));
}
}// end ValidateTest
}

@ -1,16 +1,15 @@
package com.atm.dao;
import org.junit.runners.Suite;
import org.junit.runner.RunWith;
import junit.framework.TestSuite;
import junit.framework.Test;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
import org.junit.platform.suite.api.SuiteDisplayName;
@RunWith(Suite.class)
@Suite.SuiteClasses({ com.atm.dao.LoginTest.class, com.atm.controller.ValidateTest.class })
@Suite
@SuiteDisplayName("Login Integrated Test Suite")
@SelectClasses({
com.atm.dao.LoginTest.class,
com.atm.controller.ValidateTest.class
})
public class LoginIntegratedTest {
//public static Test suit() {
// TestSuite suite = new TestSuite();
// return suite;
//}
}// end LoginITest
// This is a test suite that runs LoginTest and ValidateTest
}

@ -0,0 +1,93 @@
package com.atm.dao;
import com.atm.controller.Validate;
import com.atm.model.Customer;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ExtendWith(MockitoExtension.class)
public class LoginIntegratedTestNew {
@Mock
private ResultSet mockResultSet;
private Login login;
private Customer testCustomer;
@BeforeEach
public void setUp() {
login = new Login();
testCustomer = new Customer();
testCustomer.setCid(123456L);
testCustomer.setCpin("123456");
}
@Test
public void testLoginAndValidateIntegration() throws SQLException {
// 使用Mockito模拟静态方法DbUtil.executeQuery
try (MockedStatic<DbUtil> mockedDbUtil = mockStatic(DbUtil.class)) {
// 模拟数据库返回有效用户
mockedDbUtil.when(() -> DbUtil.executeQuery(anyString())).thenReturn(mockResultSet);
when(mockResultSet.next()).thenReturn(true);
when(mockResultSet.getString("cid")).thenReturn("123456");
when(mockResultSet.getString("cpin")).thenReturn("123456");
when(mockResultSet.getString("cname")).thenReturn("测试用户");
// 测试登录功能
boolean loginResult = login.login(testCustomer);
assertTrue(loginResult, "登录应该成功");
// 验证DbUtil.executeQuery被调用
mockedDbUtil.verify(() -> DbUtil.executeQuery("select * from customer"));
// 验证ResultSet的方法被调用
verify(mockResultSet).next();
verify(mockResultSet).getString("cid");
verify(mockResultSet).getString("cpin");
verify(mockResultSet).getString("cname");
}
// 测试验证功能
assertTrue(Validate.isNumeric("123456"), "123456应该是数字");
assertTrue(Validate.lengthValidate("123456"), "123456长度应该是6位");
assertFalse(Validate.isNumeric("abc123"), "abc123不应该是纯数字");
assertFalse(Validate.lengthValidate("123"), "123长度不应该是6位");
}
@Test
public void testLoginFailedAndValidateIntegration() throws SQLException {
// 使用Mockito模拟静态方法DbUtil.executeQuery
try (MockedStatic<DbUtil> mockedDbUtil = mockStatic(DbUtil.class)) {
// 模拟数据库返回无效用户
mockedDbUtil.when(() -> DbUtil.executeQuery(anyString())).thenReturn(mockResultSet);
when(mockResultSet.next()).thenReturn(false);
// 测试登录失败
boolean loginResult = login.login(testCustomer);
assertFalse(loginResult, "登录应该失败");
// 验证DbUtil.executeQuery被调用
mockedDbUtil.verify(() -> DbUtil.executeQuery("select * from customer"));
// 验证ResultSet的方法被调用
verify(mockResultSet).next();
}
// 测试验证功能
assertFalse(Validate.isNumeric("wrongpin"), "wrongpin不应该是纯数字");
assertFalse(Validate.lengthValidate("wrongpin"), "wrongpin长度不应该是6位");
}
}

@ -1,49 +1,70 @@
package com.atm.dao;
import org.junit.Assert;
import static org.junit.jupiter.api.Assertions.*;
import com.atm.dao.Login;
import com.atm.model.Customer;
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.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
public class LoginTest extends junit.framework.TestCase {
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
*
* @param arg0
*/
public LoginTest(String arg0) {
super(arg0);
}
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
/**
*
* @param args
*/
public static void main(String[] args) {
@ExtendWith(MockitoExtension.class)
public class LoginTest {
}
@InjectMocks
private Login login;
/**
*
* @exception Exception
*/
protected void setUp()
throws Exception {
super.setUp();
}
/**
*
* @exception Exception
*/
protected void tearDown()
throws Exception {
super.tearDown();
}
public final void testLogin() {
Assert.assertTrue(new Login().login(new Customer("123456", "123456")));
}
public final void testLogin_failed() {
Assert.assertFalse(new Login().login(new Customer("123457", "123458")));
}
}// end LoginTest
@Test
public void testLogin() throws SQLException {
try (MockedStatic<DbUtil> dbUtilMock = Mockito.mockStatic(DbUtil.class)) {
// Mock ResultSet
ResultSet mockResultSet = mock(ResultSet.class);
when(mockResultSet.next()).thenReturn(true, false); // First call returns true, second returns false
when(mockResultSet.getString("cid")).thenReturn("123456");
when(mockResultSet.getString("cpin")).thenReturn("123456");
when(mockResultSet.getString("cname")).thenReturn("张三");
// Mock DbUtil.executeQuery to return our mock ResultSet
dbUtilMock.when(() -> DbUtil.executeQuery(anyString())).thenReturn(mockResultSet);
// Create customer with matching credentials
Customer customer = new Customer();
customer.setCid(123456L);
customer.setCpin("123456");
// Test login
assertTrue(login.login(customer));
}
}
@Test
public void testLogin_failed() throws SQLException {
try (MockedStatic<DbUtil> dbUtilMock = Mockito.mockStatic(DbUtil.class)) {
// Mock ResultSet with no matching records
ResultSet mockResultSet = mock(ResultSet.class);
when(mockResultSet.next()).thenReturn(false); // No records found
// Mock DbUtil.executeQuery to return our mock ResultSet
dbUtilMock.when(() -> DbUtil.executeQuery(anyString())).thenReturn(mockResultSet);
// Create customer with non-matching credentials
Customer customer = new Customer();
customer.setCid(123457L);
customer.setCpin("123458");
// Test login
assertFalse(login.login(customer));
}
}
}

@ -61,7 +61,8 @@ public class ATMSystemIntegrationTest {
testCustomer.setCstatus("active");
testCustomer.setCtype("regular");
testCustomer = customerRepository.save(testCustomer);
// 使用customerService创建客户确保PIN码被正确加密
testCustomer = customerService.createCustomer(testCustomer);
// 创建测试账户1
testAccount1 = new Account();
@ -95,7 +96,11 @@ public class ATMSystemIntegrationTest {
// 3. 执行存款操作
BigDecimal depositAmount = new BigDecimal("1000.00");
Optional<Account> updatedAccount1 = accountService.deposit(testAccount1.getAid(), depositAmount);
boolean depositResult = accountService.deposit(testAccount1.getAid(), depositAmount);
assertTrue(depositResult);
// 验证存款后账户余额
Optional<Account> updatedAccount1 = accountService.findByAid(testAccount1.getAid());
assertTrue(updatedAccount1.isPresent());
assertEquals(new BigDecimal("6000.00"), updatedAccount1.get().getAbalance());
@ -108,7 +113,11 @@ public class ATMSystemIntegrationTest {
// 5. 执行取款操作
BigDecimal withdrawAmount = new BigDecimal("500.00");
Optional<Account> updatedAccount2 = accountService.withdraw(testAccount2.getAid(), withdrawAmount);
boolean withdrawResult = accountService.withdraw(testAccount2.getAid(), withdrawAmount);
assertTrue(withdrawResult);
// 验证取款后账户余额
Optional<Account> updatedAccount2 = accountService.findByAid(testAccount2.getAid());
assertTrue(updatedAccount2.isPresent());
assertEquals(new BigDecimal("2500.00"), updatedAccount2.get().getAbalance());
@ -140,7 +149,7 @@ public class ATMSystemIntegrationTest {
assertEquals(transferAmount, transferTransaction.getTamount());
// 9. 查询客户的所有交易记录
List<Transaction> customerTransactions = transactionService.findByCid(testCustomer.getCid());
List<Transaction> customerTransactions = transactionService.findByCustomerId(testCustomer.getCid());
assertEquals(3, customerTransactions.size());
// 10. 查询账户的所有交易记录
@ -163,8 +172,8 @@ public class ATMSystemIntegrationTest {
// 验证无法对inactive账户进行操作
BigDecimal depositAmount = new BigDecimal("100.00");
Optional<Account> depositResult = accountService.deposit(testAccount1.getAid(), depositAmount);
assertFalse(depositResult.isPresent());
boolean depositResult = accountService.deposit(testAccount1.getAid(), depositAmount);
assertFalse(depositResult);
}
@Test
@ -196,7 +205,7 @@ public class ATMSystemIntegrationTest {
assertEquals(1, account2Transactions.size());
// 查询客户的交易记录
List<Transaction> customerTransactions = transactionService.findByCid(testCustomer.getCid());
List<Transaction> customerTransactions = transactionService.findByCustomerId(testCustomer.getCid());
assertEquals(3, customerTransactions.size());
// 按交易类型查询

@ -26,13 +26,14 @@ public class AccountRepositoryTest {
private Customer createTestCustomer(Long cid, String name) {
Customer customer = new Customer();
customer.setCid(cid);
// 不再设置cid让数据库自动生成
customer.setCname(name);
customer.setCpin("123456");
customer.setCbalance(new BigDecimal("1000.00"));
customer.setCstatus("active");
customer.setCtype("regular");
entityManager.persist(customer);
entityManager.flush(); // 确保客户被持久化并生成ID
return customer;
}
@ -69,8 +70,8 @@ public class AccountRepositoryTest {
@Test
public void whenFindByCustomer_thenReturnAccounts() {
// given
Customer customer1 = createTestCustomer(12345L, "张三");
Customer customer2 = createTestCustomer(67890L, "李四");
Customer customer1 = createTestCustomer(null, "张三");
Customer customer2 = createTestCustomer(null, "李四");
Account account1 = createTestAccount(null, customer1, "checking", new BigDecimal("1000.00"));
Account account2 = createTestAccount(null, customer1, "savings", new BigDecimal("2000.00"));
@ -89,17 +90,17 @@ public class AccountRepositoryTest {
@Test
public void whenFindByCid_thenReturnAccounts() {
// given
Customer customer1 = createTestCustomer(12345L, "张三");
Customer customer2 = createTestCustomer(67890L, "李四");
Customer customer1 = createTestCustomer(null, "张三");
Customer customer2 = createTestCustomer(null, "李四");
createTestAccount(1001L, customer1, "checking", new BigDecimal("1000.00"));
createTestAccount(1002L, customer1, "savings", new BigDecimal("2000.00"));
createTestAccount(1003L, customer2, "checking", new BigDecimal("1500.00"));
createTestAccount(null, customer1, "checking", new BigDecimal("1000.00"));
createTestAccount(null, customer1, "savings", new BigDecimal("2000.00"));
createTestAccount(null, customer2, "checking", new BigDecimal("1500.00"));
entityManager.flush();
// when
List<Account> customer1Accounts = accountRepository.findByCustomerCid(12345L);
List<Account> customer2Accounts = accountRepository.findByCustomerCid(67890L);
List<Account> customer1Accounts = accountRepository.findByCustomerCid(customer1.getCid());
List<Account> customer2Accounts = accountRepository.findByCustomerCid(customer2.getCid());
// then
assertEquals(2, customer1Accounts.size());
@ -109,12 +110,12 @@ public class AccountRepositoryTest {
@Test
public void whenFindByAtype_thenReturnAccounts() {
// given
Customer customer1 = createTestCustomer(12345L, "张三");
Customer customer2 = createTestCustomer(67890L, "李四");
Customer customer1 = createTestCustomer(null, "张三");
Customer customer2 = createTestCustomer(null, "李四");
createTestAccount(1001L, customer1, "checking", new BigDecimal("1000.00"));
createTestAccount(1002L, customer1, "savings", new BigDecimal("2000.00"));
createTestAccount(1003L, customer2, "checking", new BigDecimal("1500.00"));
createTestAccount(null, customer1, "checking", new BigDecimal("1000.00"));
createTestAccount(null, customer1, "savings", new BigDecimal("2000.00"));
createTestAccount(null, customer2, "checking", new BigDecimal("1500.00"));
entityManager.flush();
// when
@ -129,18 +130,18 @@ public class AccountRepositoryTest {
@Test
public void whenFindByAstatus_thenReturnAccounts() {
// given
Customer customer1 = createTestCustomer(12345L, "张三");
Customer customer2 = createTestCustomer(67890L, "李四");
Customer customer1 = createTestCustomer(null, "张三");
Customer customer2 = createTestCustomer(null, "李四");
createTestAccount(1001L, customer1, "checking", new BigDecimal("1000.00"));
Account inactiveAccount = createTestAccount(1002L, customer1, "savings", new BigDecimal("2000.00"));
createTestAccount(null, customer1, "checking", new BigDecimal("1000.00"));
Account inactiveAccount = createTestAccount(null, customer1, "savings", new BigDecimal("2000.00"));
inactiveAccount.setAstatus("inactive");
entityManager.persist(inactiveAccount);
entityManager.flush();
// when
List<Account> activeAccounts = accountRepository.findByCustomerCidAndAstatus(12345L, "active");
List<Account> inactiveAccounts = accountRepository.findByCustomerCidAndAstatus(12345L, "inactive");
List<Account> activeAccounts = accountRepository.findByCustomerCidAndAstatus(customer1.getCid(), "active");
List<Account> inactiveAccounts = accountRepository.findByCustomerCidAndAstatus(customer1.getCid(), "inactive");
// then
assertEquals(1, activeAccounts.size());
@ -150,12 +151,12 @@ public class AccountRepositoryTest {
@Test
public void whenExistsByAid_thenReturnTrue() {
// given
Customer customer = createTestCustomer(12345L, "张三");
createTestAccount(1001L, customer, "checking", new BigDecimal("1000.00"));
Customer customer = createTestCustomer(null, "张三");
Account account = createTestAccount(null, customer, "checking", new BigDecimal("1000.00"));
entityManager.flush();
// when
boolean exists = accountRepository.existsByAid(1001L);
boolean exists = accountRepository.existsByAid(account.getAid());
// then
assertTrue(exists);
@ -173,8 +174,8 @@ public class AccountRepositoryTest {
@Test
public void whenFindByAbalanceGreaterThan_thenReturnAccounts() {
// given
Customer customer1 = createTestCustomer(12345L, "张三");
Customer customer2 = createTestCustomer(67890L, "李四");
Customer customer1 = createTestCustomer(null, "张三");
Customer customer2 = createTestCustomer(null, "李四");
Account account1 = createTestAccount(null, customer1, "checking", new BigDecimal("1000.00"));
Account account2 = createTestAccount(null, customer1, "savings", new BigDecimal("2000.00"));
@ -192,7 +193,7 @@ public class AccountRepositoryTest {
@Test
public void whenFindByAbalanceLessThan_thenReturnAccounts() {
// given
Customer customer = createTestCustomer(12345L, "张三");
Customer customer = createTestCustomer(null, "张三");
Account account1 = createTestAccount(null, customer, "checking", new BigDecimal("500.00"));
Account account2 = createTestAccount(null, customer, "savings", new BigDecimal("1500.00"));

@ -339,7 +339,7 @@ public class AccountServiceTest {
.thenReturn(Arrays.asList(testAccount));
// when
List<Account> result = accountService.findAccountsWithBalanceLessThan(new BigDecimal("6000.00"));
List<Account> result = accountService.findByAbalanceLessThan(new BigDecimal("6000.00"));
// then
assertEquals(1, result.size());

@ -0,0 +1,226 @@
package com.atm.service;
import com.atm.config.JwtTokenUtil;
import com.atm.model.Customer;
import com.atm.repository.CustomerRepository;
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.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class AuthenticationServiceTest {
@Mock
private AuthenticationManager authenticationManager;
@Mock
private JwtTokenUtil jwtTokenUtil;
@Mock
private CustomerService customerService;
@Mock
private Authentication authentication;
@Mock
private UserDetails userDetails;
@InjectMocks
private AuthenticationService authenticationService;
private Customer testCustomer;
@BeforeEach
public void setUp() {
testCustomer = new Customer();
testCustomer.setCid(12345L);
testCustomer.setCname("测试用户");
testCustomer.setCpin("123456");
testCustomer.setCstatus("active");
}
@Test
public void testAuthenticateAndGenerateToken_Success() {
// Given
Long cid = 12345L;
String cpin = "123456";
String expectedToken = "jwt.token.here";
when(authenticationManager.authenticate(any(UsernamePasswordAuthenticationToken.class)))
.thenReturn(authentication);
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getPrincipal()).thenReturn(userDetails);
when(jwtTokenUtil.generateToken(userDetails)).thenReturn(expectedToken);
// When
Optional<String> result = authenticationService.authenticateAndGenerateToken(cid, cpin);
// Then
assertTrue(result.isPresent());
assertEquals(expectedToken, result.get());
verify(authenticationManager).authenticate(any(UsernamePasswordAuthenticationToken.class));
verify(jwtTokenUtil).generateToken(userDetails);
}
@Test
public void testAuthenticateAndGenerateToken_BadCredentials() {
// Given
Long cid = 12345L;
String cpin = "wrongpin";
when(authenticationManager.authenticate(any(UsernamePasswordAuthenticationToken.class)))
.thenThrow(new BadCredentialsException("认证失败"));
// When
Optional<String> result = authenticationService.authenticateAndGenerateToken(cid, cpin);
// Then
assertFalse(result.isPresent());
verify(authenticationManager).authenticate(any(UsernamePasswordAuthenticationToken.class));
verify(jwtTokenUtil, never()).generateToken(any());
}
@Test
public void testAuthenticateAndGenerateToken_NotAuthenticated() {
// Given
Long cid = 12345L;
String cpin = "123456";
when(authenticationManager.authenticate(any(UsernamePasswordAuthenticationToken.class)))
.thenReturn(authentication);
when(authentication.isAuthenticated()).thenReturn(false);
// When
Optional<String> result = authenticationService.authenticateAndGenerateToken(cid, cpin);
// Then
assertFalse(result.isPresent());
verify(authenticationManager).authenticate(any(UsernamePasswordAuthenticationToken.class));
verify(jwtTokenUtil, never()).generateToken(any());
}
@Test
public void testValidateToken_ValidToken() {
// Given
String token = "valid.jwt.token";
String username = "12345";
when(jwtTokenUtil.getUsernameFromToken(token)).thenReturn(username);
when(customerService.loadUserByUsername(username)).thenReturn(userDetails);
when(jwtTokenUtil.validateToken(token, userDetails)).thenReturn(true);
// When
boolean result = authenticationService.validateToken(token);
// Then
assertTrue(result);
verify(jwtTokenUtil).getUsernameFromToken(token);
verify(customerService).loadUserByUsername(username);
verify(jwtTokenUtil).validateToken(token, userDetails);
}
@Test
public void testValidateToken_InvalidToken() {
// Given
String token = "invalid.jwt.token";
when(jwtTokenUtil.getUsernameFromToken(token)).thenThrow(new RuntimeException("Invalid token"));
// When
boolean result = authenticationService.validateToken(token);
// Then
assertFalse(result);
verify(jwtTokenUtil).getUsernameFromToken(token);
verify(customerService, never()).loadUserByUsername(anyString());
verify(jwtTokenUtil, never()).validateToken(any(), any());
}
@Test
public void testGetUsernameFromToken_ValidToken() {
// Given
String token = "valid.jwt.token";
String expectedUsername = "12345";
when(jwtTokenUtil.getUsernameFromToken(token)).thenReturn(expectedUsername);
// When
String result = authenticationService.getUsernameFromToken(token);
// Then
assertEquals(expectedUsername, result);
verify(jwtTokenUtil).getUsernameFromToken(token);
}
@Test
public void testGetUsernameFromToken_InvalidToken() {
// Given
String token = "invalid.jwt.token";
when(jwtTokenUtil.getUsernameFromToken(token)).thenThrow(new RuntimeException("Invalid token"));
// When
String result = authenticationService.getUsernameFromToken(token);
// Then
assertNull(result);
verify(jwtTokenUtil).getUsernameFromToken(token);
}
@Test
public void testRefreshToken_ValidToken() {
// Given
String oldToken = "valid.jwt.token";
String newToken = "new.jwt.token";
String username = "12345";
when(jwtTokenUtil.getUsernameFromToken(oldToken)).thenReturn(username);
when(customerService.loadUserByUsername(username)).thenReturn(userDetails);
when(jwtTokenUtil.validateToken(oldToken, userDetails)).thenReturn(true);
when(jwtTokenUtil.generateToken(userDetails)).thenReturn(newToken);
// When
Optional<String> result = authenticationService.refreshToken(oldToken);
// Then
assertTrue(result.isPresent());
assertEquals(newToken, result.get());
verify(jwtTokenUtil).getUsernameFromToken(oldToken);
verify(customerService).loadUserByUsername(username);
verify(jwtTokenUtil).validateToken(oldToken, userDetails);
verify(jwtTokenUtil).generateToken(userDetails);
}
@Test
public void testRefreshToken_InvalidToken() {
// Given
String invalidToken = "invalid.token.here";
when(jwtTokenUtil.getUsernameFromToken(invalidToken)).thenThrow(new RuntimeException("Invalid token"));
// When
Optional<String> result = authenticationService.refreshToken(invalidToken);
// Then
assertFalse(result.isPresent());
verify(jwtTokenUtil).getUsernameFromToken(invalidToken);
verify(customerService, never()).loadUserByUsername(anyString());
verify(jwtTokenUtil, never()).validateToken(any(), any());
verify(jwtTokenUtil, never()).generateToken(any());
}
}

@ -10,7 +10,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.math.BigDecimal;
import java.util.Optional;
@ -25,6 +25,9 @@ public class CustomerServiceTest {
@Mock
private CustomerRepository customerRepository;
@Mock
private PasswordEncoder passwordEncoder;
@InjectMocks
private CustomerService customerService;
@ -170,10 +173,8 @@ public class CustomerServiceTest {
@Test
public void whenUpdatePinWithCorrectOldPin_thenReturnTrue() {
// given
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPin = passwordEncoder.encode("123456");
testCustomer.setCpin(encodedPin);
when(passwordEncoder.matches("123456", "123456")).thenReturn(true);
when(passwordEncoder.encode("654321")).thenReturn("654321");
when(customerRepository.findByCid(12345L)).thenReturn(Optional.of(testCustomer));
when(customerRepository.save(any(Customer.class))).thenReturn(testCustomer);
@ -188,10 +189,7 @@ public class CustomerServiceTest {
@Test
public void whenUpdatePinWithIncorrectOldPin_thenReturnFalse() {
// given
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPin = passwordEncoder.encode("123456");
testCustomer.setCpin(encodedPin);
when(passwordEncoder.matches("wrongpin", "123456")).thenReturn(false);
when(customerRepository.findByCid(12345L)).thenReturn(Optional.of(testCustomer));
// when
@ -205,6 +203,7 @@ public class CustomerServiceTest {
@Test
public void whenResetPin_thenReturnTrue() {
// given
when(passwordEncoder.encode("newpin123")).thenReturn("newpin123");
when(customerRepository.findByCid(12345L)).thenReturn(Optional.of(testCustomer));
when(customerRepository.save(any(Customer.class))).thenReturn(testCustomer);

@ -90,7 +90,7 @@ public class TransactionServiceTest {
@Test
public void whenFindByFromAccount_thenReturnTransactions() {
// given
when(transactionRepository.findByFromAccount(testAccount)).thenReturn(Arrays.asList(testTransaction));
when(transactionRepository.findByFromAccountAid(testAccount.getAid())).thenReturn(Arrays.asList(testTransaction));
// when
List<Transaction> result = transactionService.findByFromAccount(testAccount);
@ -103,7 +103,7 @@ public class TransactionServiceTest {
@Test
public void whenFindByToAccount_thenReturnTransactions() {
// given
when(transactionRepository.findByToAccount(testAccount2)).thenReturn(Arrays.asList(testTransaction));
when(transactionRepository.findByToAccountAid(testAccount2.getAid())).thenReturn(Arrays.asList(testTransaction));
// when
List<Transaction> result = transactionService.findByToAccount(testAccount2);
@ -144,8 +144,24 @@ public class TransactionServiceTest {
// given
BigDecimal amount = new BigDecimal("1000.00");
String description = "Deposit test";
Account testAccount = new Account();
testAccount.setAid(1L);
when(transactionRepository.save(any(Transaction.class))).thenReturn(testTransaction);
when(accountService.findByAid(1L)).thenReturn(Optional.of(testAccount));
when(transactionRepository.save(any(Transaction.class))).thenAnswer(invocation -> {
Transaction transaction = invocation.getArgument(0);
// 创建一个新的Transaction对象确保属性正确设置
Transaction result = new Transaction();
result.setTid(1001L);
result.setFromAccount(transaction.getFromAccount());
result.setToAccount(transaction.getToAccount());
result.setTtype(transaction.getTtype());
result.setTamount(transaction.getTamount());
result.setTdescription(transaction.getTdescription());
result.setTstatus(transaction.getTstatus());
result.setCreatedAt(transaction.getCreatedAt());
return result;
});
// when
Transaction result = transactionService.createDepositTransaction(1L, amount, description);
@ -164,7 +180,7 @@ public class TransactionServiceTest {
BigDecimal amount = new BigDecimal("1000.00");
String description = "Deposit test";
when(accountService.deposit(1L, amount)).thenReturn(Optional.empty());
when(accountService.findByAid(1L)).thenReturn(Optional.empty());
// when
Transaction result = transactionService.createDepositTransaction(1L, amount, description);
@ -179,8 +195,24 @@ public class TransactionServiceTest {
// given
BigDecimal amount = new BigDecimal("500.00");
String description = "Withdrawal test";
Account testAccount = new Account();
testAccount.setAid(1L);
when(transactionRepository.save(any(Transaction.class))).thenReturn(testTransaction);
when(accountService.findByAid(1L)).thenReturn(Optional.of(testAccount));
when(transactionRepository.save(any(Transaction.class))).thenAnswer(invocation -> {
Transaction transaction = invocation.getArgument(0);
// 创建一个新的Transaction对象确保属性正确设置
Transaction result = new Transaction();
result.setTid(1002L);
result.setFromAccount(transaction.getFromAccount());
result.setToAccount(transaction.getToAccount());
result.setTtype(transaction.getTtype());
result.setTamount(transaction.getTamount());
result.setTdescription(transaction.getTdescription());
result.setTstatus(transaction.getTstatus());
result.setCreatedAt(transaction.getCreatedAt());
return result;
});
// when
Transaction result = transactionService.createWithdrawalTransaction(1L, amount, description);
@ -199,7 +231,7 @@ public class TransactionServiceTest {
BigDecimal amount = new BigDecimal("500.00");
String description = "Withdrawal test";
when(accountService.withdraw(1L, amount)).thenReturn(Optional.empty());
when(accountService.findByAid(1L)).thenReturn(Optional.empty());
// when
Transaction result = transactionService.createWithdrawalTransaction(1L, amount, description);
@ -214,8 +246,27 @@ public class TransactionServiceTest {
// given
BigDecimal amount = new BigDecimal("500.00");
String description = "Transfer test";
Account fromAccount = new Account();
fromAccount.setAid(1L);
Account toAccount = new Account();
toAccount.setAid(2L);
when(transactionRepository.save(any(Transaction.class))).thenReturn(testTransaction);
when(accountService.findByAid(1L)).thenReturn(Optional.of(fromAccount));
when(accountService.findByAid(2L)).thenReturn(Optional.of(toAccount));
when(transactionRepository.save(any(Transaction.class))).thenAnswer(invocation -> {
Transaction transaction = invocation.getArgument(0);
// 创建一个新的Transaction对象确保属性正确设置
Transaction result = new Transaction();
result.setTid(1003L);
result.setFromAccount(transaction.getFromAccount());
result.setToAccount(transaction.getToAccount());
result.setTtype(transaction.getTtype());
result.setTamount(transaction.getTamount());
result.setTdescription(transaction.getTdescription());
result.setTstatus(transaction.getTstatus());
result.setCreatedAt(transaction.getCreatedAt());
return result;
});
// when
Transaction result = transactionService.createTransferTransaction(1L, 2L, amount, description);
@ -225,6 +276,8 @@ public class TransactionServiceTest {
assertEquals("transfer", result.getTtype());
assertEquals(amount, result.getTamount());
assertEquals(description, result.getTdescription());
assertEquals(fromAccount, result.getFromAccount());
assertEquals(toAccount, result.getToAccount());
verify(transactionRepository, times(1)).save(any(Transaction.class));
}
@ -236,17 +289,14 @@ public class TransactionServiceTest {
BigDecimal amount = new BigDecimal("300.00");
String description = "Transfer test";
when(transactionRepository.save(any(Transaction.class))).thenReturn(testTransaction);
when(accountService.findByAid(fromAid)).thenReturn(Optional.empty());
// when
Transaction result = transactionService.createTransferTransaction(fromAid, toAid, amount, description);
// then
assertNotNull(result);
assertEquals("transfer", result.getTtype());
assertEquals(amount, result.getTamount());
assertEquals(description, result.getTdescription());
verify(transactionRepository, times(1)).save(any(Transaction.class));
assertNull(result);
verify(transactionRepository, never()).save(any(Transaction.class));
}
@Test
@ -255,7 +305,7 @@ public class TransactionServiceTest {
when(transactionRepository.findByAccountId(1L)).thenReturn(Arrays.asList(testTransaction));
// when
List<Transaction> result = transactionService.findByAid(1L);
List<Transaction> result = transactionService.findByAccountId(1L);
// then
assertEquals(1, result.size());
@ -268,7 +318,7 @@ public class TransactionServiceTest {
when(transactionRepository.findByCustomerId(12345L)).thenReturn(Arrays.asList(testTransaction));
// when
List<Transaction> result = transactionService.findByCid(12345L);
List<Transaction> result = transactionService.findByCustomerId(12345L);
// then
assertEquals(1, result.size());
@ -301,7 +351,7 @@ public class TransactionServiceTest {
.thenReturn(Arrays.asList(testTransaction));
// when
List<Transaction> result = transactionService.findByAmountAbove(amount);
List<Transaction> result = transactionService.findTransactionsWithAmountGreaterThan(amount);
// then
assertEquals(1, result.size());
@ -315,7 +365,7 @@ public class TransactionServiceTest {
.thenReturn(Arrays.asList(testTransaction));
// when
List<Transaction> result = transactionService.findRecentTransactionsByAccount(1L, 10);
List<Transaction> result = transactionService.findRecentTransactionsByAccountId(1L, 10);
// then
assertEquals(1, result.size());

@ -0,0 +1,156 @@
package com.atm.system;
import com.atm.model.Customer;
import com.atm.repository.CustomerRepository;
import com.atm.service.AuthenticationService;
import com.atm.service.CustomerService;
import org.junit.jupiter.api.BeforeEach;
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 org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
/**
*
*
*/
@SpringBootTest
@ActiveProfiles("test")
@Transactional
public class LoginAcceptanceTest {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private CustomerService customerService;
@Autowired
private AuthenticationService authenticationService;
private Customer testCustomer;
@BeforeEach
void setUp() {
// 创建测试客户
testCustomer = new Customer();
testCustomer.setCname("测试用户");
testCustomer.setCpin("123456"); // 明文PIN码createCustomer方法会自动加密
testCustomer.setCstatus("active"); // 使用小写与Customer.isEnabled()方法中的检查一致
testCustomer.setCtype("REGULAR");
testCustomer.setCbalance(new BigDecimal("1000.00"));
// 使用customerService创建客户确保PIN码被正确加密
testCustomer = customerService.createCustomer(testCustomer);
}
@Test
void testContextLoads() {
assertNotNull(customerRepository);
assertNotNull(passwordEncoder);
assertNotNull(customerService);
assertNotNull(authenticationService);
}
@Test
void testCreateCustomer() {
// 创建测试客户
Customer newCustomer = new Customer();
newCustomer.setCname("新用户");
newCustomer.setCpin("654321");
newCustomer.setCstatus("active"); // 使用小写与Customer.isEnabled()方法中的检查一致
newCustomer.setCtype("REGULAR");
newCustomer.setCbalance(new BigDecimal("2000.00"));
// 保存客户
Customer savedCustomer = customerService.createCustomer(newCustomer);
// 验证客户已保存
assertNotNull(savedCustomer.getCid());
assertEquals("新用户", savedCustomer.getCname());
assertTrue(passwordEncoder.matches("654321", savedCustomer.getCpin()));
}
@Test
void testCustomerLoginWithCorrectCredentials() {
// 用户使用正确的客户ID和PIN码登录
Optional<String> tokenOptional = authenticationService.authenticateAndGenerateToken(testCustomer.getCid(), "123456");
// 验证登录成功返回了JWT令牌
assertTrue(tokenOptional.isPresent());
String token = tokenOptional.get();
assertFalse(token.isEmpty());
// 验证令牌有效
assertTrue(authenticationService.validateToken(token));
assertEquals(testCustomer.getCid().toString(), authenticationService.getUsernameFromToken(token));
}
@Test
void testCustomerLoginWithIncorrectPin() {
// 用户使用正确的客户ID但错误的PIN码登录
Optional<String> tokenOptional = authenticationService.authenticateAndGenerateToken(testCustomer.getCid(), "wrongpin");
// 验证登录失败返回空的Optional
assertFalse(tokenOptional.isPresent());
}
@Test
void testCustomerLoginWithNonExistentId() {
// 用户使用不存在的客户ID登录
Long nonExistentId = 99999L;
Optional<String> tokenOptional = authenticationService.authenticateAndGenerateToken(nonExistentId, "123456");
// 验证登录失败返回空的Optional
assertFalse(tokenOptional.isPresent());
}
@Test
void testPinUpdateAndLogin() {
// 用户更新PIN码
boolean updateResult = customerService.updatePin(testCustomer.getCid(), "123456", "654321");
assertTrue(updateResult);
// 使用旧PIN码登录应该失败
Optional<String> oldTokenOptional = authenticationService.authenticateAndGenerateToken(testCustomer.getCid(), "123456");
assertFalse(oldTokenOptional.isPresent());
// 使用新PIN码登录应该成功
Optional<String> newTokenOptional = authenticationService.authenticateAndGenerateToken(testCustomer.getCid(), "654321");
assertTrue(newTokenOptional.isPresent());
String newToken = newTokenOptional.get();
assertFalse(newToken.isEmpty());
// 验证新令牌有效
assertTrue(authenticationService.validateToken(newToken));
assertEquals(testCustomer.getCid().toString(), authenticationService.getUsernameFromToken(newToken));
}
@Test
void testTokenRefresh() {
// 用户登录获取初始令牌
Optional<String> initialTokenOptional = authenticationService.authenticateAndGenerateToken(testCustomer.getCid(), "123456");
assertTrue(initialTokenOptional.isPresent());
String initialToken = initialTokenOptional.get();
// 刷新令牌
Optional<String> refreshedTokenOptional = authenticationService.refreshToken(initialToken);
assertTrue(refreshedTokenOptional.isPresent());
String refreshedToken = refreshedTokenOptional.get();
assertFalse(refreshedToken.isEmpty());
// 验证刷新后的令牌有效
assertTrue(authenticationService.validateToken(refreshedToken));
assertEquals(testCustomer.getCid().toString(), authenticationService.getUsernameFromToken(refreshedToken));
}
}

@ -0,0 +1,118 @@
package com.atm.system;
import com.atm.controller.AuthController;
import com.atm.service.AuthenticationService;
import com.atm.service.CustomerService;
import com.fasterxml.jackson.databind.ObjectMapper;
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.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
*
*/
@WebMvcTest(AuthController.class)
@ActiveProfiles("test")
public class LoginSystemTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private AuthenticationService authenticationService;
@MockBean
private CustomerService customerService;
@MockBean
private AuthenticationManager authenticationManager;
@MockBean
private com.atm.config.JwtTokenUtil jwtTokenUtil;
@MockBean
private org.springframework.security.core.userdetails.UserDetailsService userDetailsService;
@MockBean
private PasswordEncoder passwordEncoder;
private Map<String, Object> loginRequest;
@BeforeEach
void setUp() {
loginRequest = new HashMap<>();
loginRequest.put("cid", 12345L);
loginRequest.put("cpin", "123456");
}
@Test
void testLoginSuccessSystemFlow() throws Exception {
// 模拟认证成功
when(authenticationService.authenticateAndGenerateToken(any(), anyString()))
.thenReturn(java.util.Optional.of("mock-jwt-token"));
mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token").value("mock-jwt-token"));
}
@Test
void testLoginFailureSystemFlow() throws Exception {
// 模拟认证失败
when(authenticationService.authenticateAndGenerateToken(any(), anyString()))
.thenReturn(java.util.Optional.empty());
mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isUnauthorized());
}
@Test
void testLoginWithWrongPinSystemFlow() throws Exception {
// 设置错误的PIN码
loginRequest.put("cpin", "wrongpin");
// 模拟认证失败
when(authenticationService.authenticateAndGenerateToken(any(), anyString()))
.thenReturn(java.util.Optional.empty());
mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isUnauthorized());
}
@Test
void testLoginWithInvalidCidFormatSystemFlow() throws Exception {
// 设置无效的客户ID格式
loginRequest.put("cid", "invalid-cid");
mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isBadRequest());
}
}

@ -0,0 +1,91 @@
package com.atm.system;
import com.atm.controller.AuthController;
import com.atm.service.AuthenticationService;
import com.atm.service.CustomerService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
*
*/
@SpringBootTest
@AutoConfigureWebMvc
@ActiveProfiles("test")
public class SimpleLoginSystemTest {
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private AuthenticationService authenticationService;
@MockBean
private CustomerService customerService;
@MockBean
private AuthenticationManager authenticationManager;
private MockMvc mockMvc;
private Map<String, Object> loginRequest;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
loginRequest = new HashMap<>();
loginRequest.put("cid", 12345L);
loginRequest.put("cpin", "123456");
}
@Test
@WithMockUser
void testLoginSuccessSystemFlow() throws Exception {
// 模拟认证成功
when(authenticationService.authenticateAndGenerateToken(12345L, "123456"))
.thenReturn(java.util.Optional.of("mock-jwt-token"));
mockMvc.perform(post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token").value("mock-jwt-token"));
}
@Test
@WithMockUser
void testLoginFailureSystemFlow() throws Exception {
// 模拟认证失败
when(authenticationService.authenticateAndGenerateToken(12345L, "123456"))
.thenReturn(java.util.Optional.empty());
mockMvc.perform(post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isUnauthorized());
}
}

@ -10,4 +10,7 @@ spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# 日志配置
logging.level.com.atm=DEBUG
logging.level.com.atm=DEBUG
# 允许Bean定义覆盖
spring.main.allow-bean-definition-overriding=true

@ -401,7 +401,19 @@
**目标**基于EA模型使用TRAE生成并开发登录功能代码提交至Gitea。
1. **TRAE关联Gitea仓库**
- 打开TRAE客户端选择"克隆仓库"输入Gitea仓库地址`http://localhost:3000/gitea/cstatm-mte.git`,克隆至本地目录(如`D:\cstatm-mte`)。
- **情况1克隆已有仓库**
- 打开TRAE客户端选择"克隆仓库"输入Gitea仓库地址`http://localhost:3000/gitea/cstatm-mte.git`,克隆至本地目录(如`D:\cstatm-mte`)。
- **情况2本地已有项目关联远程仓库**
- 打开TRAE客户端选择"打开文件夹",选择本地项目目录
- 在TRAE终端中执行以下命令关联远程仓库
```bash
git init # 初始化本地仓库(如果未初始化)
git add . # 添加所有文件到暂存区
git commit -m "初始提交" # 提交本地代码
git remote add origin http://localhost:3000/gitea/cstatm-mte.git # 添加远程仓库
git push -u origin main # 推送代码到远程仓库并设置上游分支
```
2. **TRAE生成Spring Boot项目**
- 在TRAE中新建"Spring Boot项目",技术栈选择:
@ -483,10 +495,17 @@
```
4. **基于EA模型生成核心类**
- 输入提示:"基于阶段1的EA类图设计生成登录功能相关的类包括界面类、控制类、数据模型类、数据访问类和服务类确保与模型一致"
- TRAE自动生成代码后手动补充配置类和启动类
- `SecurityConfig`:密码加密配置
- `AtmApplication`Spring Boot启动类
- **EAEnterprise Architect模型设计工具**
- 用于UML建模包括需求图、用例图、活动图、时序图和类图等
- 在阶段1中已完成登录功能的完整模型设计定义了系统架构和类关系
- EA模型文件`cstatm-mte.eap`,包含所有设计元素和约束关系
- **TRAEAI代码生成工具**
- 基于EA模型生成Java代码将设计模型转换为可执行代码
- 输入提示:"基于阶段1的EA类图设计生成登录功能相关的类包括界面类、控制类、数据模型类、数据访问类和服务类确保与模型一致"
- TRAE自动生成代码后手动补充配置类和启动类
- `SecurityConfig`:密码加密配置
- `AtmApplication`Spring Boot启动类
5. **实现登录核心逻辑与阶段1模型一致**
- `UserService.authenticateUser()`对应EA模型中的UserService类方法查询数据库用户通过BCrypt加密比对密码

Loading…
Cancel
Save