Compare commits

..

4 Commits

Author SHA1 Message Date
David
d0f0de67ca feature ci
Some checks failed
CI/CD Pipeline for Spring Boot / build-and-test (push) Failing after 5m48s
CI/CD Pipeline for Spring Boot / docker (push) Has been skipped
CI/CD Pipeline for Spring Boot / Deploy - Docker - serveur (push) Has been skipped
2025-09-12 12:01:20 +02:00
David
428e656598 Merge branch 'dev'
Some checks failed
CI/CD Pipeline for Spring Boot / docker (push) Blocked by required conditions
CI/CD Pipeline for Spring Boot / Deploy - Docker - serveur (push) Blocked by required conditions
CI/CD Pipeline for Spring Boot / build-and-test (push) Has been cancelled
2025-09-12 12:00:08 +02:00
David
b473c4cbe5 feature ci
Some checks failed
CI/CD Pipeline for Spring Boot Dev / docker (push) Has been cancelled
CI/CD Pipeline for Spring Boot Dev / Deploy - Docker - serveur (push) Has been cancelled
CI/CD Pipeline for Spring Boot Dev / build-and-test (push) Has been cancelled
2025-09-12 11:59:48 +02:00
David
1e0b50bd86 feature ci 2025-09-12 11:59:35 +02:00
31 changed files with 244 additions and 991 deletions

105
.gitea/workflows/ci.yml Executable file
View File

@ -0,0 +1,105 @@
name: CI/CD Pipeline for Spring Boot
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
# Step 1: Checkout code
- name: Checkout Code
uses: actions/checkout@v3
# Step 2: Set up JDK
- name: Set up JDK 23
uses: actions/setup-java@v3
with:
java-version: 23
distribution: 'temurin'
# Step 3: Cache Maven dependencies
- name: Cache Maven Dependencies
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# Step 4: Build and test
- name: Build and Test
run: |
./mvnw clean verify
docker:
runs-on: ubuntu-latest
needs: [ build-and-test ]
steps:
- name: Install Docker
run: |
apt-get update
apt-get install -y docker.io
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Download buildx
run: |
mkdir -p ~/.docker/cli-plugins
curl -sL https://github.com/docker/buildx/releases/download/v0.11.2/buildx-v0.11.2.linux-amd64 -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
- name: Setup buildx
run: |
docker buildx create --use
docker buildx inspect --bootstrap
- name: Login to Cloud Coding Registry
uses: docker/login-action@v2
with:
registry: rg.fr-par.scw.cloud/weworkstudio
username: nologin
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push
uses: docker/build-push-action@master
with:
context: .
file: ./Dockerfile
push: true
tags: rg.fr-par.scw.cloud/weworkstudio/xpeditis-backend:prod
build-args: |
XPEDITIS_PROFILE=prod
- name: Cleanup buildx
run: |
docker buildx rm
- name: Docker cleanup
run: docker system prune -af
- name: Uninstall Docker
run: |
apt-get purge -y docker.io
apt-get autoremove -y --purge docker.io
rm -rf /var/lib/docker /etc/docker
deploy_server:
name: Deploy - Docker - serveur
runs-on: ubuntu-latest
needs: [ docker ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Déclencher le Webhook
run: |
curl -X POST -H "Content-Type:application/json" -d '{"data": "example" }' ${{ secrets.WEBHOOK_URL }}

94
.gitea/workflows/dev.yml Normal file
View File

@ -0,0 +1,94 @@
name: CI/CD Pipeline for Spring Boot Dev
on:
push:
branches:
- dev
pull_request:
branches:
- dev
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
# Step 1: Checkout code
- name: Checkout Code
uses: actions/checkout@v3
# Step 2: Set up JDK
- name: Set up JDK 23
uses: actions/setup-java@v3
with:
java-version: 23
distribution: 'temurin'
# Step 3: Cache Maven dependencies
- name: Cache Maven Dependencies
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# Step 4: Build and test
- name: Build and Test
run: |
./mvnw clean verify
docker:
runs-on: ubuntu-latest
needs: [ build-and-test ]
steps:
- name: Install Docker
run: |
apt-get update
apt-get install -y docker.io
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Download buildx
run: |
mkdir -p ~/.docker/cli-plugins
curl -sL https://github.com/docker/buildx/releases/download/v0.11.2/buildx-v0.11.2.linux-amd64 -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
- name: Setup buildx
run: |
docker buildx create --use
docker buildx inspect --bootstrap
- name: Login to Cloud Coding Registry
uses: docker/login-action@v2
with:
registry: rg.fr-par.scw.cloud/weworkstudio
username: nologin
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push
uses: docker/build-push-action@master
with:
context: .
file: ./Dockerfile
push: true
tags: rg.fr-par.scw.cloud/weworkstudio/xpeditis-backend:dev
build-args: |
XPEDITIS_PROFILE=dev
- name: Cleanup buildx
run: |
docker buildx rm
- name: Docker cleanup
run: docker system prune -af
- name: Uninstall Docker
run: |
apt-get purge -y docker.io
apt-get autoremove -y --purge docker.io
rm -rf /var/lib/docker /etc/docker

View File

@ -11,8 +11,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@ -35,11 +33,4 @@ public class AuthenticationRestController {
@RequestBody @Valid RegisterRequest request) {
return ResponseEntity.ok(service.register(request));
}
// Optional: simple endpoint to start OAuth2 authorization if frontend needs a redirect URL
@GetMapping("/oauth2/authorization")
public ResponseEntity<Void> oauth2Authorization(@RequestParam("provider") String provider) {
// Frontend should redirect to /oauth2/authorization/{provider}
return ResponseEntity.status(302).header("Location", "/oauth2/authorization/" + provider).build();
}
}

View File

@ -1,59 +0,0 @@
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

@ -2,7 +2,6 @@ package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.UserService;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -11,14 +10,8 @@ import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.security.Principal;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@ -42,35 +35,4 @@ public class UserRestController {
service.changePassword(request, connectedUser);
return new ResponseEntity<>(HttpStatus.OK);
}
@Operation(summary = "Create a user")
@PostMapping
public ResponseEntity<UserAccount> create(@RequestBody UserAccount user) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(user));
}
@Operation(summary = "Update a user")
@PutMapping("/{id}")
public ResponseEntity<UserAccount> update(@PathVariable Long id, @RequestBody UserAccount user) {
return ResponseEntity.ok(service.update(id, user));
}
@Operation(summary = "Get a user by id")
@GetMapping("/{id}")
public ResponseEntity<UserAccount> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List users")
@GetMapping
public ResponseEntity<List<UserAccount>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete a user")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -1,39 +0,0 @@
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

@ -1,39 +0,0 @@
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

@ -1,49 +0,0 @@
package com.dh7789dev.xpeditis.configuration;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.entity.UserEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private final UserDao userDao;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
Map<String, Object> attributes = oAuth2User.getAttributes();
String registrationId = userRequest.getClientRegistration().getRegistrationId();
String email;
if ("google".equals(registrationId)) {
email = (String) attributes.get("email");
} else if ("linkedin".equals(registrationId)) {
email = (String) attributes.getOrDefault("email", attributes.get("emailAddress"));
} else {
throw new OAuth2AuthenticationException("Unsupported provider: " + registrationId);
}
if (email == null) {
throw new OAuth2AuthenticationException("Email not provided by OAuth2 provider.");
}
UserEntity user = userDao.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("No local user bound to email: " + email));
return new DefaultOAuth2User(user.getAuthorities(), attributes, "email");
}
}

View File

@ -88,8 +88,8 @@ public class GlobalConfiguration {
@Bean
public UserDetailsService userDetailsService() {
return identifier -> userDao.findByUsernameOrEmail(identifier)
.orElseThrow(() -> new UsernameNotFoundException(String.format(USER_NOT_FOUND_MSG, identifier)));
return username -> userDao.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(String.format(USER_NOT_FOUND_MSG, username)));
}
}

View File

@ -45,9 +45,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
final String jwt;
final String username;
if (request.getServletPath().contains("/api/v1/auth") ||
request.getServletPath().startsWith("/oauth2/") ||
request.getServletPath().startsWith("/login/oauth2/")) {
if (request.getServletPath().contains("/api/v1/auth")) {
filterChain.doFilter(request, response);
return;
}

View File

@ -1,51 +0,0 @@
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.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final UserDao userDao;
private final TokenDao tokenDao;
private final JwtUtil jwtUtil;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String email = authentication.getName();
UserEntity user = userDao.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User not found for email: " + email));
String accessToken = jwtUtil.generateToken(user);
String refreshToken = jwtUtil.generateRefreshToken(user);
// Save token (like classic login)
TokenEntity tokenEntity = new TokenEntity()
.setUser(user)
.setToken(accessToken)
.setTokenType(TokenEntity.Type.BEARER)
.setExpired(false)
.setRevoked(false);
tokenDao.save(tokenEntity);
response.setContentType("application/json");
response.getWriter().write("{\"access_token\":\"" + accessToken + "\", \"refresh_token\":\"" + refreshToken + "\"}");
response.getWriter().flush();
}
}

View File

@ -21,7 +21,6 @@ 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;
@ -35,15 +34,13 @@ public class SecurityConfiguration {
boolean csrfEnabled;
private static final String ADMIN_ROLE = "ADMIN";
// private static final String ADMIN_PLATFORM_ROLE = "ADMIN_PLATFORM";
private static final String ADMIN_PLATFORM_ROLE = "ADMIN_PLATFORM";
private static final String API_V1_URI = "/api/v1/**";
private static final String[] WHITE_LIST_URL = {
"/api/v1/auth/**",
"/actuator/health/**",
"/oauth2/**",
"/login/oauth2/**"};
"/actuator/health/**"};
private static final String[] INTERNAL_WHITE_LIST_URL = {
"/v2/api-docs",
@ -64,20 +61,11 @@ public class SecurityConfiguration {
private final UserDetailsService userDetailsService;
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,
@Autowired(required = false) ClientRegistrationRepository clientRegistrationRepository) {
public SecurityConfiguration(UserDetailsService userDetailsService, LogoutHandler logoutHandler) {
this.userDetailsService = userDetailsService;
this.logoutHandler = logoutHandler;
this.customOAuth2UserService = customOAuth2UserService;
this.oAuth2AuthenticationSuccessHandler = oAuth2AuthenticationSuccessHandler;
this.clientRegistrationRepository = clientRegistrationRepository;
}
@Bean
@ -122,20 +110,13 @@ public class SecurityConfiguration {
)
.sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
if (clientRegistrationRepository != null) {
http.oauth2Login(oauth -> oauth
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService))
.successHandler(oAuth2AuthenticationSuccessHandler)
);
}
http
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.logout(logout -> logout
.logoutUrl("/api/v1/auth/logout")
.addLogoutHandler(logoutHandler)
.logoutSuccessHandler((req, res, auth) -> SecurityContextHolder.clearContext()));
.logoutSuccessHandler((request,
response,
authentication) -> SecurityContextHolder.clearContext()));
return http.build();
}

View File

@ -1,24 +1,3 @@
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:
@ -68,7 +47,6 @@ spring:
timeout: 3000
writetimeout: 5000
application:
email:
from: randommailjf@gmail.com

View File

@ -51,28 +51,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:
from: contact@leblr.fr
@ -86,24 +64,3 @@ application:
expiration: 86400000 # a day
refresh-token:
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

@ -0,0 +1,14 @@
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

@ -1,12 +0,0 @@
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

@ -1,40 +0,0 @@
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

@ -1,49 +0,0 @@
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,17 +1,4 @@
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,22 +1,10 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import java.security.Principal;
import java.util.List;
public interface UserService {
void changePassword(ChangePasswordRequest request, Principal connectedUser);
UserAccount create(UserAccount user);
UserAccount update(Long id, UserAccount user);
UserAccount getById(Long id);
List<UserAccount> list();
void delete(Long id);
}

View File

@ -1,40 +1,7 @@
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,11 +1,9 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import org.springframework.stereotype.Service;
import java.security.Principal;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@ -20,29 +18,4 @@ public class UserServiceImpl implements UserService {
public void changePassword(ChangePasswordRequest request, Principal connectedUser) {
userRepository.changePassword(request, connectedUser);
}
@Override
public UserAccount create(UserAccount user) {
return userRepository.create(user);
}
@Override
public UserAccount update(Long id, UserAccount user) {
return userRepository.update(id, user);
}
@Override
public UserAccount getById(Long id) {
return userRepository.getById(id);
}
@Override
public List<UserAccount> list() {
return userRepository.list();
}
@Override
public void delete(Long id) {
userRepository.delete(id);
}
}

View File

@ -1,17 +1,4 @@
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,22 +1,10 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import java.security.Principal;
import java.util.List;
public interface UserRepository {
void changePassword(ChangePasswordRequest request, Principal connectedUser);
UserAccount create(UserAccount user);
UserAccount update(Long id, UserAccount user);
UserAccount getById(Long id);
List<UserAccount> list();
void delete(Long id);
}

View File

@ -42,10 +42,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>

View File

@ -6,16 +6,12 @@ import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
public interface UserDao extends JpaRepository<UserEntity, Long> {
@Query("SELECT u FROM UserEntity u WHERE u.username = :username")
Optional<UserEntity> findByUsername(String username);
boolean existsByUsername(String username);
Optional<UserEntity> findByEmail(String email);
boolean existsByEmail(String email);
@Query("SELECT u FROM UserEntity u WHERE u.username = :identifier OR u.email = :identifier")
Optional<UserEntity> findByUsernameOrEmail(String identifier);
}

View File

@ -33,7 +33,7 @@ public class AuthenticationJwtRepository implements AuthenticationRepository {
@Override
public AuthenticationResponse authenticate(AuthenticationRequest request) {
log.info("identifier: {}", request.getUsername());
log.info("username: {}, password: {}", request.getUsername(), request.getPassword());
UsernamePasswordAuthenticationToken authToken = UsernamePasswordAuthenticationToken
.unauthenticated(request.getUsername(), request.getPassword());
Authentication authentication = authenticationManager.authenticate(authToken);
@ -42,7 +42,7 @@ public class AuthenticationJwtRepository implements AuthenticationRepository {
throw new UsernameNotFoundException("Failed to authenticate");
}
var userEntity = userDao.findByUsernameOrEmail(request.getUsername()).orElseThrow();
var userEntity = userDao.findByUsername(request.getUsername()).orElseThrow();
var jwtToken = jwtUtil.generateToken(userEntity);
var refreshToken = jwtUtil.generateRefreshToken(userEntity);

View File

@ -1,94 +1,10 @@
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

@ -4,18 +4,12 @@ import com.dh7789dev.xpeditis.UserRepository;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.entity.UserEntity;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import com.dh7789dev.xpeditis.entity.Role;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;
import java.security.Principal;
import java.util.List;
import java.util.stream.Collectors;
@Repository
public class UserJpaRepository implements UserRepository {
@ -23,15 +17,11 @@ public class UserJpaRepository implements UserRepository {
private final UserDao userDao;
private final PasswordEncoder passwordEncoder;
private final CompanyDao companyDao;
private final UserMapper userMapper;
@Autowired
public UserJpaRepository(UserDao userDao, PasswordEncoder passwordEncoder, CompanyDao companyDao, UserMapper userMapper) {
public UserJpaRepository(UserDao userDao, PasswordEncoder passwordEncoder) {
this.userDao = userDao;
this.passwordEncoder = passwordEncoder;
this.companyDao = companyDao;
this.userMapper = userMapper;
}
@Override
@ -52,55 +42,4 @@ public class UserJpaRepository implements UserRepository {
userEntity.setPassword(passwordEncoder.encode(request.getNewPassword()));
userDao.save(userEntity);
}
@Override
public UserAccount create(UserAccount user) {
UserEntity entity = userMapper.userAccountToUserEntity(user);
if (user.getPassword() != null) {
entity.setPassword(passwordEncoder.encode(user.getPassword()));
}
if (user.getRole() != null) {
entity.setRole(Role.valueOf(user.getRole()));
}
if (user.getCompany() != null && user.getCompany().getId() != null) {
companyDao.findById(user.getCompany().getId()).ifPresent(entity::setCompany);
}
entity.setEnabled(true);
UserEntity saved = userDao.save(entity);
return userMapper.userEntityToUserAccount(saved);
}
@Override
public UserAccount update(Long id, UserAccount user) {
UserEntity entity = userDao.findById(id).orElseThrow();
if (user.getFirstName() != null) entity.setFirstName(user.getFirstName());
if (user.getLastName() != null) entity.setLastName(user.getLastName());
if (user.getEmail() != null) entity.setEmail(user.getEmail());
if (user.getUsername() != null) entity.setUsername(user.getUsername());
if (user.getRole() != null) entity.setRole(Role.valueOf(user.getRole()));
if (user.getCompany() != null && user.getCompany().getId() != null) {
companyDao.findById(user.getCompany().getId()).ifPresent(entity::setCompany);
}
UserEntity saved = userDao.save(entity);
return userMapper.userEntityToUserAccount(saved);
}
@Override
public UserAccount getById(Long id) {
return userDao.findById(id)
.map(userMapper::userEntityToUserAccount)
.orElseThrow();
}
@Override
public List<UserAccount> list() {
return userDao.findAll().stream()
.map(userMapper::userEntityToUserAccount)
.collect(Collectors.toList());
}
@Override
public void delete(Long id) {
userDao.deleteById(id);
}
}

View File

@ -1,123 +1,19 @@
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.mockito.MockitoAnnotations;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@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 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);
void test(){
int test = 1 +1;
assertEquals(2,test);
}
}

View File

@ -1,141 +1,19 @@
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.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 org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
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.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@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 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);
void test(){
int test = 1 +1;
assertEquals(2,test);
}
}