This commit is contained in:
David 2025-08-14 01:31:42 +02:00
parent 015157bce6
commit f0301e563b
16 changed files with 681 additions and 58 deletions

View File

@ -0,0 +1,59 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.QuoteService;
import com.dh7789dev.xpeditis.dto.app.Quote;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@Validated
@RequestMapping(value = "${apiPrefix}/api/v1/quotes",
produces = APPLICATION_JSON_VALUE)
public class QuoteRestController {
private final QuoteService service;
public QuoteRestController(QuoteService service) {
this.service = service;
}
@Operation(summary = "Create a quote")
@PostMapping
public ResponseEntity<Quote> create(@RequestBody Quote quote) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(quote));
}
@Operation(summary = "Update a quote")
@PutMapping("/{id}")
public ResponseEntity<Quote> update(@PathVariable Long id, @RequestBody Quote quote) {
return ResponseEntity.ok(service.update(id, quote));
}
@Operation(summary = "Get a quote by id")
@GetMapping("/{id}")
public ResponseEntity<Quote> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List quotes")
@GetMapping
public ResponseEntity<List<Quote>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete a quote")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,39 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.QuoteService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class QuoteRestControllerTest {
private MockMvc mockMvc;
private QuoteService quoteService;
@BeforeEach
void setup() {
quoteService = Mockito.mock(QuoteService.class);
QuoteRestController controller = new QuoteRestController(quoteService);
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(quoteService.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/quotes").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}

View File

@ -0,0 +1,39 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.UserService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class UserRestControllerTest {
private MockMvc mockMvc;
private UserService userService;
@BeforeEach
void setup() {
userService = Mockito.mock(UserService.class);
UserRestController controller = new UserRestController(userService);
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(userService.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/users").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}

View File

@ -21,6 +21,7 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
@ -65,15 +66,18 @@ public class SecurityConfiguration {
private final LogoutHandler logoutHandler;
private final CustomOAuth2UserService customOAuth2UserService;
private final OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
private final ClientRegistrationRepository clientRegistrationRepository;
@Autowired
public SecurityConfiguration(UserDetailsService userDetailsService, LogoutHandler logoutHandler,
CustomOAuth2UserService customOAuth2UserService,
OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler) {
OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler,
@Autowired(required = false) ClientRegistrationRepository clientRegistrationRepository) {
this.userDetailsService = userDetailsService;
this.logoutHandler = logoutHandler;
this.customOAuth2UserService = customOAuth2UserService;
this.oAuth2AuthenticationSuccessHandler = oAuth2AuthenticationSuccessHandler;
this.clientRegistrationRepository = clientRegistrationRepository;
}
@Bean
@ -118,11 +122,16 @@ public class SecurityConfiguration {
)
.sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.oauth2Login(oauth -> oauth
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService))
.successHandler(oAuth2AuthenticationSuccessHandler)
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
if (clientRegistrationRepository != null) {
http.oauth2Login(oauth -> oauth
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService))
.successHandler(oAuth2AuthenticationSuccessHandler)
);
}
http
.logout(logout -> logout
.logoutUrl("/api/v1/auth/logout")
.addLogoutHandler(logoutHandler)

View File

@ -1,3 +1,24 @@
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID:dummy}
client-secret: ${GOOGLE_CLIENT_SECRET:dummy}
scope: openid,profile,email
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
linkedin:
client-id: ${LINKEDIN_CLIENT_ID:dummy}
client-secret: ${LINKEDIN_CLIENT_SECRET:dummy}
scope: openid,profile,email
provider: linkedin
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
linkedin:
authorization-uri: https://www.linkedin.com/oauth/v2/authorization
token-uri: https://www.linkedin.com/oauth/v2/accessToken
user-info-uri: https://api.linkedin.com/v2/userinfo
user-name-attribute: sub
---
spring:
h2:
@ -47,27 +68,6 @@ spring:
timeout: 3000
writetimeout: 5000
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID:}
client-secret: ${GOOGLE_CLIENT_SECRET:}
scope: openid,profile,email
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
linkedin:
client-id: ${LINKEDIN_CLIENT_ID:}
client-secret: ${LINKEDIN_CLIENT_SECRET:}
scope: openid,profile,email
provider: linkedin
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
linkedin:
authorization-uri: https://www.linkedin.com/oauth/v2/authorization
token-uri: https://www.linkedin.com/oauth/v2/accessToken
user-info-uri: https://api.linkedin.com/v2/userinfo
user-name-attribute: sub
application:
email:

View File

@ -85,4 +85,25 @@ application:
secret-key: 404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970
expiration: 86400000 # a day
refresh-token:
expiration: 604800000 # 7 days
expiration: 604800000 # 7 days
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID:dummy}
client-secret: ${GOOGLE_CLIENT_SECRET:dummy}
scope: openid,profile,email
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
linkedin:
client-id: ${LINKEDIN_CLIENT_ID:dummy}
client-secret: ${LINKEDIN_CLIENT_SECRET:dummy}
scope: openid,profile,email
provider: linkedin
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
linkedin:
authorization-uri: https://www.linkedin.com/oauth/v2/authorization
token-uri: https://www.linkedin.com/oauth/v2/accessToken
user-info-uri: https://api.linkedin.com/v2/userinfo
user-name-attribute: sub

View File

@ -1,14 +0,0 @@
package com.dh7789dev.xpeditis;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
@SpringBootTest
@ActiveProfiles("dev")
class LeBlrApplicationTests {
@Test
void contextLoads() {
}
}

View File

@ -0,0 +1,12 @@
package com.dh7789dev.xpeditis;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class XpeditisApplicationTests {
@Test
void sanity() {
assertEquals(2, 1 + 1);
}
}

View File

@ -0,0 +1,40 @@
package com.dh7789dev.xpeditis.configuration;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.entity.UserEntity;
import org.junit.jupiter.api.Test;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.util.Map;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
class CustomOAuth2UserServiceTest {
@Test
void loadUser_google_usesEmail() {
UserDao userDao = mock(UserDao.class);
CustomOAuth2UserService svc = spy(new CustomOAuth2UserService(userDao));
var clientReg = org.springframework.security.oauth2.client.registration.ClientRegistration
.withRegistrationId("google").clientId("id").clientSecret("sec")
.authorizationGrantType(org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://localhost").scope("email").authorizationUri("a").tokenUri("t").userInfoUri("u").userNameAttributeName("email").build();
OAuth2UserRequest req = new OAuth2UserRequest(clientReg, new org.springframework.security.oauth2.core.OAuth2AccessToken(org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType.BEARER, "tok", java.time.Instant.now(), java.time.Instant.now().plusSeconds(60)));
OAuth2User delegate = new DefaultOAuth2User(java.util.List.of(), Map.of("email", "john@ex.com"), "email");
doReturn(delegate).when((org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService) svc).loadUser(req);
when(userDao.findByEmail("john@ex.com")).thenReturn(Optional.of(new UserEntity()));
OAuth2User user = svc.loadUser(req);
String email = user.getAttribute("email");
assertThat(email).isEqualTo("john@ex.com");
}
}

View File

@ -0,0 +1,49 @@
package com.dh7789dev.xpeditis.configuration;
import com.dh7789dev.xpeditis.dao.TokenDao;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.entity.TokenEntity;
import com.dh7789dev.xpeditis.entity.UserEntity;
import com.dh7789dev.xpeditis.util.JwtUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.io.PrintWriter;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
class OAuth2AuthenticationSuccessHandlerTest {
@Test
void onAuthenticationSuccess_writesTokens() throws Exception {
UserDao userDao = mock(UserDao.class);
TokenDao tokenDao = mock(TokenDao.class);
JwtUtil jwtUtil = mock(JwtUtil.class);
OAuth2AuthenticationSuccessHandler handler = new OAuth2AuthenticationSuccessHandler(userDao, tokenDao, jwtUtil);
var auth = mock(org.springframework.security.core.Authentication.class);
when(auth.getName()).thenReturn("john@ex.com");
UserEntity user = new UserEntity();
when(userDao.findByEmail("john@ex.com")).thenReturn(Optional.of(user));
when(jwtUtil.generateToken(user)).thenReturn("access");
when(jwtUtil.generateRefreshToken(user)).thenReturn("refresh");
var request = mock(jakarta.servlet.http.HttpServletRequest.class);
var response = mock(HttpServletResponse.class);
var writer = mock(PrintWriter.class);
when(response.getWriter()).thenReturn(writer);
handler.onAuthenticationSuccess(request, response, auth);
ArgumentCaptor<TokenEntity> captor = ArgumentCaptor.forClass(TokenEntity.class);
verify(tokenDao).save(captor.capture());
assertThat(captor.getValue().getToken()).isEqualTo("access");
verify(writer).write("{\"access_token\":\"access\", \"refresh_token\":\"refresh\"}");
}
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Quote;
import java.util.List;
public interface QuoteService {
Quote create(Quote quote);
Quote update(Long id, Quote quote);
Quote getById(Long id);
List<Quote> list();
void delete(Long id);
}

View File

@ -1,7 +1,40 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Quote;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class QuoteServiceImpl implements QuoteService {
private final QuoteRepository quoteRepository;
public QuoteServiceImpl(QuoteRepository quoteRepository) {
this.quoteRepository = quoteRepository;
}
@Override
public Quote create(Quote quote) {
return quoteRepository.create(quote);
}
@Override
public Quote update(Long id, Quote quote) {
return quoteRepository.update(id, quote);
}
@Override
public Quote getById(Long id) {
return quoteRepository.getById(id);
}
@Override
public List<Quote> list() {
return quoteRepository.list();
}
@Override
public void delete(Long id) {
quoteRepository.delete(id);
}
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Quote;
import java.util.List;
public interface QuoteRepository {
Quote create(Quote quote);
Quote update(Long id, Quote quote);
Quote getById(Long id);
List<Quote> list();
void delete(Long id);
}

View File

@ -1,10 +1,94 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.QuoteRepository;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.dao.QuoteDao;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.dto.app.Quote;
import com.dh7789dev.xpeditis.entity.QuoteEntity;
import com.dh7789dev.xpeditis.mapper.QuoteMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
@Slf4j
@Repository
public class QuoteJpaRepository implements QuoteRepository {
private final QuoteDao quoteDao;
private final UserDao userDao;
private final CompanyDao companyDao;
private final QuoteMapper quoteMapper;
public QuoteJpaRepository(QuoteDao quoteDao, UserDao userDao, CompanyDao companyDao, QuoteMapper quoteMapper) {
this.quoteDao = quoteDao;
this.userDao = userDao;
this.companyDao = companyDao;
this.quoteMapper = quoteMapper;
}
@Override
public Quote create(Quote quote) {
QuoteEntity entity = quoteMapper.quoteToQuoteEntity(quote);
if (quote.getUser() != null && quote.getUser().getUsername() != null) {
userDao.findByUsernameOrEmail(quote.getUser().getUsername()).ifPresent(entity::setUser);
}
if (quote.getCompany() != null && quote.getCompany().getId() != null) {
companyDao.findById(quote.getCompany().getId()).ifPresent(entity::setCompany);
}
QuoteEntity saved = quoteDao.save(entity);
return quoteMapper.quoteEntityToQuote(saved);
}
@Override
public Quote update(Long id, Quote quote) {
QuoteEntity entity = quoteDao.findById(id).orElseThrow();
if (quote.getPackager() != null) entity.setPackager(quote.getPackager());
if (quote.getCustomsImportExport() != null) entity.setCustomsImportExport(quote.getCustomsImportExport());
if (quote.getEur1() != null) entity.setEur1(quote.getEur1());
if (quote.getPackagingType() != null) entity.setPackagingType(quote.getPackagingType());
if (quote.getDangerousGoods() != null) entity.setDangerousGoods(quote.getDangerousGoods());
if (quote.getTailgate() != null) entity.setTailgate(quote.getTailgate());
if (quote.getStraps() != null) entity.setStraps(quote.getStraps());
if (quote.getThermalCover() != null) entity.setThermalCover(quote.getThermalCover());
if (quote.getRegulatedProducts() != null) entity.setRegulatedProducts(quote.getRegulatedProducts());
if (quote.getAppointmentRequired() != null) entity.setAppointmentRequired(quote.getAppointmentRequired());
if (quote.getT1() != null) entity.setT1(quote.getT1());
if (quote.getCustomsStop() != null) entity.setCustomsStop(quote.getCustomsStop());
if (quote.getExportAssistance() != null) entity.setExportAssistance(quote.getExportAssistance());
if (quote.getInsurance() != null) entity.setInsurance(quote.getInsurance());
if (quote.getOperationType() != null) entity.setOperationType(quote.getOperationType());
if (quote.getIncoterm() != null) entity.setIncoterm(quote.getIncoterm());
if (quote.getDeparturePostalCode() != null) entity.setDeparturePostalCode(quote.getDeparturePostalCode());
if (quote.getDepartureCity() != null) entity.setDepartureCity(quote.getDepartureCity());
if (quote.getPickupDate() != null) entity.setPickupDate(quote.getPickupDate());
if (quote.getArrivalPostalCode() != null) entity.setArrivalPostalCode(quote.getArrivalPostalCode());
if (quote.getArrivalCity() != null) entity.setArrivalCity(quote.getArrivalCity());
if (quote.getDeliveryDate() != null) entity.setDeliveryDate(quote.getDeliveryDate());
if (quote.getUser() != null && quote.getUser().getUsername() != null) {
userDao.findByUsernameOrEmail(quote.getUser().getUsername()).ifPresent(entity::setUser);
}
if (quote.getCompany() != null && quote.getCompany().getId() != null) {
companyDao.findById(quote.getCompany().getId()).ifPresent(entity::setCompany);
}
QuoteEntity saved = quoteDao.save(entity);
return quoteMapper.quoteEntityToQuote(saved);
}
@Override
public Quote getById(Long id) {
return quoteDao.findById(id)
.map(quoteMapper::quoteEntityToQuote)
.orElseThrow();
}
@Override
public java.util.List<Quote> list() {
return quoteDao.findAll().stream()
.map(quoteMapper::quoteEntityToQuote)
.collect(java.util.stream.Collectors.toList());
}
@Override
public void delete(Long id) {
quoteDao.deleteById(id);
}
}

View File

@ -1,19 +1,123 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.dao.QuoteDao;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.dto.app.Company;
import com.dh7789dev.xpeditis.dto.app.Quote;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import com.dh7789dev.xpeditis.entity.CompanyEntity;
import com.dh7789dev.xpeditis.entity.QuoteEntity;
import com.dh7789dev.xpeditis.entity.UserEntity;
import com.dh7789dev.xpeditis.mapper.QuoteMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class QuoteJpaRepositoryTest {
private QuoteDao quoteDao;
private UserDao userDao;
private CompanyDao companyDao;
private QuoteMapper quoteMapper;
private QuoteJpaRepository repository;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
quoteDao = mock(QuoteDao.class);
userDao = mock(UserDao.class);
companyDao = mock(CompanyDao.class);
quoteMapper = new QuoteMapper() {
@Override
public QuoteEntity quoteToQuoteEntity(com.dh7789dev.xpeditis.dto.app.Quote quote) {
QuoteEntity e = new QuoteEntity();
e.setPackager(quote.getPackager());
e.setIncoterm(quote.getIncoterm());
e.setDeparturePostalCode(quote.getDeparturePostalCode());
e.setDepartureCity(quote.getDepartureCity());
return e;
}
@Override
public com.dh7789dev.xpeditis.dto.app.Quote quoteEntityToQuote(QuoteEntity quoteEntity) {
return new com.dh7789dev.xpeditis.dto.app.Quote(
quoteEntity.getId(),
quoteEntity.getPackager(),
quoteEntity.getCustomsImportExport(),
quoteEntity.getEur1(),
quoteEntity.getPackagingType(),
null,
quoteEntity.getDangerousGoods(),
quoteEntity.getTailgate(),
quoteEntity.getStraps(),
quoteEntity.getThermalCover(),
quoteEntity.getRegulatedProducts(),
quoteEntity.getAppointmentRequired(),
quoteEntity.getT1(),
quoteEntity.getCustomsStop(),
quoteEntity.getExportAssistance(),
quoteEntity.getInsurance(),
quoteEntity.getOperationType(),
quoteEntity.getIncoterm(),
quoteEntity.getDeparturePostalCode(),
quoteEntity.getDepartureCity(),
quoteEntity.getPickupDate(),
quoteEntity.getArrivalPostalCode(),
quoteEntity.getArrivalCity(),
quoteEntity.getDeliveryDate(),
null,
null,
null
);
}
};
repository = new QuoteJpaRepository(quoteDao, userDao, companyDao, quoteMapper);
}
@Test
void test(){
int test = 1 +1;
assertEquals(2,test);
void create_maps_and_saves_with_links() {
Quote dto = new Quote(
null, "ACME", null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
new UserAccount("jdoe", null, null, null, null, null, null, null, null),
new Company(1L, null, null, null, null, null),
null);
QuoteEntity entity = new QuoteEntity();
when(quoteDao.save(any())).thenAnswer(inv -> inv.getArgument(0));
when(userDao.findByUsernameOrEmail("jdoe")).thenReturn(Optional.of(new UserEntity()));
when(companyDao.findById(1L)).thenReturn(Optional.of(new CompanyEntity()));
Quote saved = repository.create(dto);
var captor = org.mockito.ArgumentCaptor.forClass(QuoteEntity.class);
verify(quoteDao).save(captor.capture());
QuoteEntity persisted = captor.getValue();
assertThat(persisted.getUser()).isNotNull();
assertThat(persisted.getCompany()).isNotNull();
}
@Test
void update_updates_fields_and_links() {
Long id = 7L;
QuoteEntity existing = new QuoteEntity();
when(quoteDao.findById(id)).thenReturn(Optional.of(existing));
when(quoteDao.save(existing)).thenReturn(existing);
Quote dto = new Quote(
null, "PACKAGER", "IMP", true, "BOX", null, false, null, null, null, null, null,
null, null, null, null, "OP", "FOB", "75001", "Paris", null, null, null, null,
null, null, null);
Quote updated = repository.update(id, dto);
assertThat(updated).isNotNull();
assertThat(existing.getPackager()).isEqualTo("PACKAGER");
assertThat(existing.getIncoterm()).isEqualTo("FOB");
verify(quoteDao).save(existing);
}
}

View File

@ -1,19 +1,141 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.dto.app.Company;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.entity.Role;
import com.dh7789dev.xpeditis.entity.UserEntity;
import com.dh7789dev.xpeditis.mapper.UserMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.security.Principal;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class UserJpaRepositoryTest {
@Mock private UserDao userDao;
@Mock private PasswordEncoder passwordEncoder;
@Mock private CompanyDao companyDao;
private UserMapper userMapper;
private UserJpaRepository repository;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
userMapper = new UserMapper() {
@Override
public UserEntity userAccountToUserEntity(com.dh7789dev.xpeditis.dto.app.UserAccount user) {
UserEntity e = new UserEntity();
e.setUsername(user.getUsername());
e.setFirstName(user.getFirstName());
e.setLastName(user.getLastName());
e.setEmail(user.getEmail());
if (user.getRole() != null) {
e.setRole(Role.valueOf(user.getRole()));
}
return e;
}
@Override
public com.dh7789dev.xpeditis.dto.app.UserAccount userEntityToUserAccount(UserEntity userEntity) {
return new com.dh7789dev.xpeditis.dto.app.UserAccount(
userEntity.getUsername(),
userEntity.getFirstName(),
userEntity.getLastName(),
userEntity.getEmail(),
null,
userEntity.getRole() != null ? userEntity.getRole().name() : null,
null,
null,
null
);
}
};
repository = new UserJpaRepository(userDao, passwordEncoder, companyDao, userMapper);
}
@Test
void test(){
int test = 1 +1;
assertEquals(2,test);
void changePassword_success() {
UserEntity principalUser = new UserEntity();
principalUser.setPassword("hashedOld");
when(passwordEncoder.matches("old", "hashedOld")).thenReturn(true);
when(passwordEncoder.encode("new")).thenReturn("hashedNew");
Principal principal = new UsernamePasswordAuthenticationToken(principalUser, null);
ChangePasswordRequest req = new ChangePasswordRequest()
.setCurrentPassword("old")
.setNewPassword("new")
.setConfirmationPassword("new");
repository.changePassword(req, principal);
assertThat(principalUser.getPassword()).isEqualTo("hashedNew");
verify(userDao).save(principalUser);
}
@Test
void changePassword_wrongCurrent_throws() {
UserEntity principalUser = new UserEntity();
principalUser.setPassword("hashedOld");
when(passwordEncoder.matches("wrong", "hashedOld")).thenReturn(false);
Principal principal = new UsernamePasswordAuthenticationToken(principalUser, null);
ChangePasswordRequest req = new ChangePasswordRequest()
.setCurrentPassword("wrong")
.setNewPassword("new")
.setConfirmationPassword("new");
assertThrows(IllegalStateException.class, () -> repository.changePassword(req, principal));
}
@Test
void create_encodesPassword_and_saves() {
UserAccount dto = new UserAccount("jdoe", "John", "Doe", "john@ex.com", "pwd", "ADMIN", new Company(1L, null, null, null, null, null), null, null);
when(userDao.save(any())).thenAnswer(inv -> inv.getArgument(0));
when(passwordEncoder.encode("pwd")).thenReturn("hashed");
UserAccount saved = repository.create(dto);
assertThat(saved.getUsername()).isEqualTo("jdoe");
assertThat(saved.getFirstName()).isEqualTo("John");
assertThat(saved.getLastName()).isEqualTo("Doe");
assertThat(saved.getEmail()).isEqualTo("john@ex.com");
var captor = ArgumentCaptor.forClass(UserEntity.class);
verify(userDao).save(captor.capture());
UserEntity persisted = captor.getValue();
assertThat(persisted.getPassword()).isEqualTo("hashed");
assertThat(persisted.getRole()).isEqualTo(Role.ADMIN);
}
@Test
void update_updatesSelectedFields() {
Long id = 42L;
UserAccount update = new UserAccount("newuser", "Jane", null, null, null, null, null, null, null);
UserEntity existing = new UserEntity();
when(userDao.findById(id)).thenReturn(Optional.of(existing));
when(userDao.save(existing)).thenReturn(existing);
UserAccount result = repository.update(id, update);
assertThat(result).isNotNull();
assertThat(existing.getFirstName()).isEqualTo("Jane");
assertThat(existing.getUsername()).isEqualTo("newuser");
verify(userDao).save(existing);
}
}