mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 17:49:57 +01:00
refactor(test): gemeinsame AbstractIntegrationTest Base-Class
Alle 9 Integration-Test-Klassen nutzen eine gemeinsame Base-Class mit geteiltem Spring Context (MOCK statt RANDOM_PORT), vorberechnetem BCrypt-Hash und gemeinsamen Helper-Methoden. Eliminiert ~600 Zeilen Duplikation und Runtime-BCrypt-Aufrufe im Test-Setup.
This commit is contained in:
parent
5219c93dd1
commit
7b1c114693
9 changed files with 195 additions and 803 deletions
|
|
@ -0,0 +1,108 @@
|
||||||
|
package de.effigenix.infrastructure;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import de.effigenix.domain.usermanagement.RoleName;
|
||||||
|
import de.effigenix.domain.usermanagement.UserStatus;
|
||||||
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
||||||
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
||||||
|
import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository;
|
||||||
|
import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@ActiveProfiles("test")
|
||||||
|
@Transactional
|
||||||
|
public abstract class AbstractIntegrationTest {
|
||||||
|
|
||||||
|
protected static final String BCRYPT_PASS123 = "$2a$10$4tNfKjz7w.67G72CVop9CuuzBh5vrkYJki8lZ66ZwwLtQjlrm6hHy";
|
||||||
|
protected static final String TEST_PASSWORD = "Pass123";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected MockMvc mockMvc;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected UserJpaRepository userRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected RoleJpaRepository roleRepository;
|
||||||
|
|
||||||
|
@Value("${jwt.secret}")
|
||||||
|
protected String jwtSecret;
|
||||||
|
|
||||||
|
@Value("${jwt.expiration}")
|
||||||
|
protected long jwtExpiration;
|
||||||
|
|
||||||
|
protected String generateToken(String userId, String username, String permissions) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
||||||
|
jwtSecret.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Jwts.builder()
|
||||||
|
.subject(userId)
|
||||||
|
.claim("username", username)
|
||||||
|
.claim("permissions", permissions)
|
||||||
|
.issuedAt(new Date(now))
|
||||||
|
.expiration(new Date(now + jwtExpiration))
|
||||||
|
.signWith(key)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String generateExpiredToken(String userId, String username) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
||||||
|
jwtSecret.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Jwts.builder()
|
||||||
|
.subject(userId)
|
||||||
|
.claim("username", username)
|
||||||
|
.claim("permissions", "")
|
||||||
|
.issuedAt(new Date(now - 10000))
|
||||||
|
.expiration(new Date(now - 5000))
|
||||||
|
.signWith(key)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String generateRefreshToken(String userId, String username) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
||||||
|
jwtSecret.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Jwts.builder()
|
||||||
|
.subject(userId)
|
||||||
|
.claim("username", username)
|
||||||
|
.claim("type", "refresh")
|
||||||
|
.issuedAt(new Date(now))
|
||||||
|
.expiration(new Date(now + 7200000))
|
||||||
|
.signWith(key)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RoleEntity createRole(RoleName roleName, String description) {
|
||||||
|
RoleEntity role = new RoleEntity(
|
||||||
|
UUID.randomUUID().toString(), roleName, Set.of(), description);
|
||||||
|
return roleRepository.save(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UserEntity createUser(String username, String email, Set<RoleEntity> roles, String branchId) {
|
||||||
|
UserEntity user = new UserEntity(
|
||||||
|
UUID.randomUUID().toString(), username, email,
|
||||||
|
BCRYPT_PASS123, roles,
|
||||||
|
branchId, UserStatus.ACTIVE, LocalDateTime.now(), null);
|
||||||
|
return userRepository.save(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,31 +1,16 @@
|
||||||
package de.effigenix.infrastructure.inventory.web;
|
package de.effigenix.infrastructure.inventory.web;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import de.effigenix.domain.usermanagement.RoleName;
|
import de.effigenix.domain.usermanagement.RoleName;
|
||||||
import de.effigenix.domain.usermanagement.UserStatus;
|
import de.effigenix.infrastructure.AbstractIntegrationTest;
|
||||||
import de.effigenix.infrastructure.inventory.web.dto.CreateStorageLocationRequest;
|
import de.effigenix.infrastructure.inventory.web.dto.CreateStorageLocationRequest;
|
||||||
import de.effigenix.infrastructure.inventory.web.dto.UpdateStorageLocationRequest;
|
import de.effigenix.infrastructure.inventory.web.dto.UpdateStorageLocationRequest;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -40,33 +25,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
* - Story 1.1 – Lagerort anlegen
|
* - Story 1.1 – Lagerort anlegen
|
||||||
* - Story 1.2 – Lagerort bearbeiten und (de-)aktivieren
|
* - Story 1.2 – Lagerort bearbeiten und (de-)aktivieren
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@ActiveProfiles("test")
|
|
||||||
@Transactional
|
|
||||||
@DisplayName("StorageLocation Controller Integration Tests")
|
@DisplayName("StorageLocation Controller Integration Tests")
|
||||||
class StorageLocationControllerIntegrationTest {
|
class StorageLocationControllerIntegrationTest extends AbstractIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserJpaRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RoleJpaRepository roleRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PasswordEncoder passwordEncoder;
|
|
||||||
|
|
||||||
@Value("${jwt.secret}")
|
|
||||||
private String jwtSecret;
|
|
||||||
|
|
||||||
@Value("${jwt.expiration}")
|
|
||||||
private long jwtExpiration;
|
|
||||||
|
|
||||||
private String adminToken;
|
private String adminToken;
|
||||||
private String readerToken;
|
private String readerToken;
|
||||||
|
|
@ -74,35 +34,16 @@ class StorageLocationControllerIntegrationTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
RoleEntity adminRole = new RoleEntity(
|
RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin");
|
||||||
UUID.randomUUID().toString(), RoleName.ADMIN, Set.of(), "Admin");
|
RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer");
|
||||||
roleRepository.save(adminRole);
|
|
||||||
|
|
||||||
RoleEntity viewerRole = new RoleEntity(
|
UserEntity admin = createUser("inv.admin", "inv.admin@test.com", Set.of(adminRole), "BRANCH-01");
|
||||||
UUID.randomUUID().toString(), RoleName.PRODUCTION_WORKER, Set.of(), "Viewer");
|
UserEntity reader = createUser("inv.reader", "inv.reader@test.com", Set.of(viewerRole), "BRANCH-01");
|
||||||
roleRepository.save(viewerRole);
|
UserEntity viewer = createUser("inv.viewer", "inv.viewer@test.com", Set.of(viewerRole), "BRANCH-01");
|
||||||
|
|
||||||
String adminId = UUID.randomUUID().toString();
|
adminToken = generateToken(admin.getId(), "inv.admin", "STOCK_WRITE,STOCK_READ");
|
||||||
userRepository.save(new UserEntity(
|
readerToken = generateToken(reader.getId(), "inv.reader", "STOCK_READ");
|
||||||
adminId, "inv.admin", "inv.admin@test.com",
|
viewerToken = generateToken(viewer.getId(), "inv.viewer", "USER_READ");
|
||||||
passwordEncoder.encode("Pass123"), Set.of(adminRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
String readerId = UUID.randomUUID().toString();
|
|
||||||
userRepository.save(new UserEntity(
|
|
||||||
readerId, "inv.reader", "inv.reader@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(viewerRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
String viewerId = UUID.randomUUID().toString();
|
|
||||||
userRepository.save(new UserEntity(
|
|
||||||
viewerId, "inv.viewer", "inv.viewer@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(viewerRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
adminToken = generateToken(adminId, "inv.admin", "STOCK_WRITE,STOCK_READ");
|
|
||||||
readerToken = generateToken(readerId, "inv.reader", "STOCK_READ");
|
|
||||||
viewerToken = generateToken(viewerId, "inv.viewer", "USER_READ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Lagerort anlegen – Pflichtfelder ====================
|
// ==================== Lagerort anlegen – Pflichtfelder ====================
|
||||||
|
|
@ -622,18 +563,4 @@ class StorageLocationControllerIntegrationTest {
|
||||||
|
|
||||||
return objectMapper.readTree(result.getResponse().getContentAsString()).get("id").asText();
|
return objectMapper.readTree(result.getResponse().getContentAsString()).get("id").asText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateToken(String userId, String username, String permissions) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
|
||||||
jwtSecret.getBytes(StandardCharsets.UTF_8));
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("permissions", permissions)
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + jwtExpiration))
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,19 @@
|
||||||
package de.effigenix.infrastructure.masterdata.web;
|
package de.effigenix.infrastructure.masterdata.web;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import de.effigenix.domain.masterdata.PriceModel;
|
import de.effigenix.domain.masterdata.PriceModel;
|
||||||
import de.effigenix.domain.masterdata.Unit;
|
import de.effigenix.domain.masterdata.Unit;
|
||||||
import de.effigenix.domain.usermanagement.RoleName;
|
import de.effigenix.domain.usermanagement.RoleName;
|
||||||
import de.effigenix.domain.usermanagement.UserStatus;
|
import de.effigenix.infrastructure.AbstractIntegrationTest;
|
||||||
import de.effigenix.infrastructure.masterdata.web.dto.*;
|
import de.effigenix.infrastructure.masterdata.web.dto.*;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
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.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -40,33 +26,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
*
|
*
|
||||||
* Abgedeckte Testfälle: TC-ART-01 bis TC-ART-11
|
* Abgedeckte Testfälle: TC-ART-01 bis TC-ART-11
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@ActiveProfiles("test")
|
|
||||||
@Transactional
|
|
||||||
@DisplayName("Article Controller Integration Tests")
|
@DisplayName("Article Controller Integration Tests")
|
||||||
class ArticleControllerIntegrationTest {
|
class ArticleControllerIntegrationTest extends AbstractIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserJpaRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RoleJpaRepository roleRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PasswordEncoder passwordEncoder;
|
|
||||||
|
|
||||||
@Value("${jwt.secret}")
|
|
||||||
private String jwtSecret;
|
|
||||||
|
|
||||||
@Value("${jwt.expiration}")
|
|
||||||
private long jwtExpiration;
|
|
||||||
|
|
||||||
private String adminToken;
|
private String adminToken;
|
||||||
private String viewerToken;
|
private String viewerToken;
|
||||||
|
|
@ -74,28 +35,14 @@ class ArticleControllerIntegrationTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() throws Exception {
|
void setUp() throws Exception {
|
||||||
RoleEntity adminRole = new RoleEntity(
|
RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin");
|
||||||
UUID.randomUUID().toString(), RoleName.ADMIN, Set.of(), "Admin");
|
RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer");
|
||||||
roleRepository.save(adminRole);
|
|
||||||
|
|
||||||
RoleEntity viewerRole = new RoleEntity(
|
UserEntity admin = createUser("art.admin", "art.admin@test.com", Set.of(adminRole), "BRANCH-01");
|
||||||
UUID.randomUUID().toString(), RoleName.PRODUCTION_WORKER, Set.of(), "Viewer");
|
UserEntity viewer = createUser("art.viewer", "art.viewer@test.com", Set.of(viewerRole), "BRANCH-01");
|
||||||
roleRepository.save(viewerRole);
|
|
||||||
|
|
||||||
String adminId = UUID.randomUUID().toString();
|
adminToken = generateToken(admin.getId(), "art.admin", "MASTERDATA_WRITE");
|
||||||
userRepository.save(new UserEntity(
|
viewerToken = generateToken(viewer.getId(), "art.viewer", "USER_READ");
|
||||||
adminId, "art.admin", "art.admin@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(adminRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
String viewerId = UUID.randomUUID().toString();
|
|
||||||
userRepository.save(new UserEntity(
|
|
||||||
viewerId, "art.viewer", "art.viewer@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(viewerRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
adminToken = generateToken(adminId, "art.admin", "MASTERDATA_WRITE");
|
|
||||||
viewerToken = generateToken(viewerId, "art.viewer", "USER_READ");
|
|
||||||
|
|
||||||
// Vorbedingung: Kategorie erstellen
|
// Vorbedingung: Kategorie erstellen
|
||||||
categoryId = createCategory("Obst & Gemüse");
|
categoryId = createCategory("Obst & Gemüse");
|
||||||
|
|
@ -414,18 +361,4 @@ class ArticleControllerIntegrationTest {
|
||||||
.andReturn();
|
.andReturn();
|
||||||
return objectMapper.readTree(result.getResponse().getContentAsString()).get("id").asText();
|
return objectMapper.readTree(result.getResponse().getContentAsString()).get("id").asText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateToken(String userId, String username, String permissions) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
|
||||||
jwtSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("permissions", permissions)
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + jwtExpiration))
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,19 @@
|
||||||
package de.effigenix.infrastructure.masterdata.web;
|
package de.effigenix.infrastructure.masterdata.web;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import de.effigenix.domain.masterdata.*;
|
import de.effigenix.domain.masterdata.*;
|
||||||
import de.effigenix.domain.usermanagement.RoleName;
|
import de.effigenix.domain.usermanagement.RoleName;
|
||||||
import de.effigenix.domain.usermanagement.UserStatus;
|
import de.effigenix.infrastructure.AbstractIntegrationTest;
|
||||||
import de.effigenix.infrastructure.masterdata.web.dto.*;
|
import de.effigenix.infrastructure.masterdata.web.dto.*;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
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.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -41,61 +27,22 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
*
|
*
|
||||||
* Abgedeckte Testfälle: TC-CUS-01 bis TC-CUS-11, TC-B2B-01/02, TC-AUTH
|
* Abgedeckte Testfälle: TC-CUS-01 bis TC-CUS-11, TC-B2B-01/02, TC-AUTH
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@ActiveProfiles("test")
|
|
||||||
@Transactional
|
|
||||||
@DisplayName("Customer Controller Integration Tests")
|
@DisplayName("Customer Controller Integration Tests")
|
||||||
class CustomerControllerIntegrationTest {
|
class CustomerControllerIntegrationTest extends AbstractIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserJpaRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RoleJpaRepository roleRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PasswordEncoder passwordEncoder;
|
|
||||||
|
|
||||||
@Value("${jwt.secret}")
|
|
||||||
private String jwtSecret;
|
|
||||||
|
|
||||||
@Value("${jwt.expiration}")
|
|
||||||
private long jwtExpiration;
|
|
||||||
|
|
||||||
private String adminToken;
|
private String adminToken;
|
||||||
private String viewerToken;
|
private String viewerToken;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
RoleEntity adminRole = new RoleEntity(
|
RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin");
|
||||||
UUID.randomUUID().toString(), RoleName.ADMIN, Set.of(), "Admin");
|
RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer");
|
||||||
roleRepository.save(adminRole);
|
|
||||||
|
|
||||||
RoleEntity viewerRole = new RoleEntity(
|
UserEntity admin = createUser("cus.admin", "cus.admin@test.com", Set.of(adminRole), "BRANCH-01");
|
||||||
UUID.randomUUID().toString(), RoleName.PRODUCTION_WORKER, Set.of(), "Viewer");
|
UserEntity viewer = createUser("cus.viewer", "cus.viewer@test.com", Set.of(viewerRole), "BRANCH-01");
|
||||||
roleRepository.save(viewerRole);
|
|
||||||
|
|
||||||
String adminId = UUID.randomUUID().toString();
|
adminToken = generateToken(admin.getId(), "cus.admin", "MASTERDATA_WRITE");
|
||||||
userRepository.save(new UserEntity(
|
viewerToken = generateToken(viewer.getId(), "cus.viewer", "USER_READ");
|
||||||
adminId, "cus.admin", "cus.admin@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(adminRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
String viewerId = UUID.randomUUID().toString();
|
|
||||||
userRepository.save(new UserEntity(
|
|
||||||
viewerId, "cus.viewer", "cus.viewer@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(viewerRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
adminToken = generateToken(adminId, "cus.admin", "MASTERDATA_WRITE");
|
|
||||||
viewerToken = generateToken(viewerId, "cus.viewer", "USER_READ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== TC-CUS-01: B2C-Kunde erstellen ====================
|
// ==================== TC-CUS-01: B2C-Kunde erstellen ====================
|
||||||
|
|
@ -538,18 +485,4 @@ class CustomerControllerIntegrationTest {
|
||||||
.andReturn();
|
.andReturn();
|
||||||
return objectMapper.readTree(artResult.getResponse().getContentAsString()).get("id").asText();
|
return objectMapper.readTree(artResult.getResponse().getContentAsString()).get("id").asText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateToken(String userId, String username, String permissions) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
|
||||||
jwtSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("permissions", permissions)
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + jwtExpiration))
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,17 @@
|
||||||
package de.effigenix.infrastructure.masterdata.web;
|
package de.effigenix.infrastructure.masterdata.web;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import de.effigenix.domain.usermanagement.RoleName;
|
||||||
import de.effigenix.domain.usermanagement.UserStatus;
|
import de.effigenix.infrastructure.AbstractIntegrationTest;
|
||||||
import de.effigenix.infrastructure.masterdata.web.dto.CreateProductCategoryRequest;
|
import de.effigenix.infrastructure.masterdata.web.dto.CreateProductCategoryRequest;
|
||||||
import de.effigenix.infrastructure.masterdata.web.dto.UpdateProductCategoryRequest;
|
import de.effigenix.infrastructure.masterdata.web.dto.UpdateProductCategoryRequest;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository;
|
|
||||||
import de.effigenix.domain.usermanagement.RoleName;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
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.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -38,61 +24,22 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
*
|
*
|
||||||
* Abgedeckte Testfälle: TC-CAT-01 bis TC-CAT-06
|
* Abgedeckte Testfälle: TC-CAT-01 bis TC-CAT-06
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@ActiveProfiles("test")
|
|
||||||
@Transactional
|
|
||||||
@DisplayName("ProductCategory Controller Integration Tests")
|
@DisplayName("ProductCategory Controller Integration Tests")
|
||||||
class ProductCategoryControllerIntegrationTest {
|
class ProductCategoryControllerIntegrationTest extends AbstractIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserJpaRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RoleJpaRepository roleRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PasswordEncoder passwordEncoder;
|
|
||||||
|
|
||||||
@Value("${jwt.secret}")
|
|
||||||
private String jwtSecret;
|
|
||||||
|
|
||||||
@Value("${jwt.expiration}")
|
|
||||||
private long jwtExpiration;
|
|
||||||
|
|
||||||
private String adminToken;
|
private String adminToken;
|
||||||
private String viewerToken;
|
private String viewerToken;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
RoleEntity adminRole = new RoleEntity(
|
RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin");
|
||||||
UUID.randomUUID().toString(), RoleName.ADMIN, Set.of(), "Admin");
|
RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer");
|
||||||
roleRepository.save(adminRole);
|
|
||||||
|
|
||||||
RoleEntity viewerRole = new RoleEntity(
|
UserEntity admin = createUser("cat.admin", "cat.admin@test.com", Set.of(adminRole), "BRANCH-01");
|
||||||
UUID.randomUUID().toString(), RoleName.PRODUCTION_WORKER, Set.of(), "Viewer");
|
UserEntity viewer = createUser("cat.viewer", "cat.viewer@test.com", Set.of(viewerRole), "BRANCH-01");
|
||||||
roleRepository.save(viewerRole);
|
|
||||||
|
|
||||||
String adminId = UUID.randomUUID().toString();
|
adminToken = generateToken(admin.getId(), "cat.admin", "MASTERDATA_WRITE");
|
||||||
userRepository.save(new UserEntity(
|
viewerToken = generateToken(viewer.getId(), "cat.viewer", "USER_READ");
|
||||||
adminId, "cat.admin", "cat.admin@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(adminRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
String viewerId = UUID.randomUUID().toString();
|
|
||||||
userRepository.save(new UserEntity(
|
|
||||||
viewerId, "cat.viewer", "cat.viewer@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(viewerRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
adminToken = generateToken(adminId, "cat.admin", "MASTERDATA_WRITE");
|
|
||||||
viewerToken = generateToken(viewerId, "cat.viewer", "USER_READ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== TC-CAT-01: Kategorie erstellen (Happy Path) ====================
|
// ==================== TC-CAT-01: Kategorie erstellen (Happy Path) ====================
|
||||||
|
|
@ -274,18 +221,4 @@ class ProductCategoryControllerIntegrationTest {
|
||||||
.andReturn();
|
.andReturn();
|
||||||
return objectMapper.readTree(result.getResponse().getContentAsString()).get("id").asText();
|
return objectMapper.readTree(result.getResponse().getContentAsString()).get("id").asText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateToken(String userId, String username, String permissions) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
|
||||||
jwtSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("permissions", permissions)
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + jwtExpiration))
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,17 @@
|
||||||
package de.effigenix.infrastructure.masterdata.web;
|
package de.effigenix.infrastructure.masterdata.web;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import de.effigenix.domain.usermanagement.RoleName;
|
import de.effigenix.domain.usermanagement.RoleName;
|
||||||
import de.effigenix.domain.usermanagement.UserStatus;
|
import de.effigenix.infrastructure.AbstractIntegrationTest;
|
||||||
import de.effigenix.infrastructure.masterdata.web.dto.*;
|
import de.effigenix.infrastructure.masterdata.web.dto.*;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
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.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -38,61 +24,22 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
*
|
*
|
||||||
* Abgedeckte Testfälle: TC-SUP-01 bis TC-SUP-12, TC-AUTH-01/02
|
* Abgedeckte Testfälle: TC-SUP-01 bis TC-SUP-12, TC-AUTH-01/02
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@ActiveProfiles("test")
|
|
||||||
@Transactional
|
|
||||||
@DisplayName("Supplier Controller Integration Tests")
|
@DisplayName("Supplier Controller Integration Tests")
|
||||||
class SupplierControllerIntegrationTest {
|
class SupplierControllerIntegrationTest extends AbstractIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserJpaRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RoleJpaRepository roleRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PasswordEncoder passwordEncoder;
|
|
||||||
|
|
||||||
@Value("${jwt.secret}")
|
|
||||||
private String jwtSecret;
|
|
||||||
|
|
||||||
@Value("${jwt.expiration}")
|
|
||||||
private long jwtExpiration;
|
|
||||||
|
|
||||||
private String adminToken;
|
private String adminToken;
|
||||||
private String viewerToken;
|
private String viewerToken;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
RoleEntity adminRole = new RoleEntity(
|
RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin");
|
||||||
UUID.randomUUID().toString(), RoleName.ADMIN, Set.of(), "Admin");
|
RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer");
|
||||||
roleRepository.save(adminRole);
|
|
||||||
|
|
||||||
RoleEntity viewerRole = new RoleEntity(
|
UserEntity admin = createUser("sup.admin", "sup.admin@test.com", Set.of(adminRole), "BRANCH-01");
|
||||||
UUID.randomUUID().toString(), RoleName.PRODUCTION_WORKER, Set.of(), "Viewer");
|
UserEntity viewer = createUser("sup.viewer", "sup.viewer@test.com", Set.of(viewerRole), "BRANCH-01");
|
||||||
roleRepository.save(viewerRole);
|
|
||||||
|
|
||||||
String adminId = UUID.randomUUID().toString();
|
adminToken = generateToken(admin.getId(), "sup.admin", "MASTERDATA_WRITE");
|
||||||
userRepository.save(new UserEntity(
|
viewerToken = generateToken(viewer.getId(), "sup.viewer", "USER_READ");
|
||||||
adminId, "sup.admin", "sup.admin@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(adminRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
String viewerId = UUID.randomUUID().toString();
|
|
||||||
userRepository.save(new UserEntity(
|
|
||||||
viewerId, "sup.viewer", "sup.viewer@test.com",
|
|
||||||
passwordEncoder.encode("Pass123"), Set.of(viewerRole),
|
|
||||||
"BRANCH-01", UserStatus.ACTIVE, LocalDateTime.now(), null));
|
|
||||||
|
|
||||||
adminToken = generateToken(adminId, "sup.admin", "MASTERDATA_WRITE");
|
|
||||||
viewerToken = generateToken(viewerId, "sup.viewer", "USER_READ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== TC-SUP-01: Lieferant erstellen – Pflichtfelder ====================
|
// ==================== TC-SUP-01: Lieferant erstellen – Pflichtfelder ====================
|
||||||
|
|
@ -449,18 +396,4 @@ class SupplierControllerIntegrationTest {
|
||||||
.andReturn();
|
.andReturn();
|
||||||
return objectMapper.readTree(result.getResponse().getContentAsString()).get("id").asText();
|
return objectMapper.readTree(result.getResponse().getContentAsString()).get("id").asText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateToken(String userId, String username, String permissions) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
|
||||||
jwtSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("permissions", permissions)
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + jwtExpiration))
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,19 @@
|
||||||
package de.effigenix.infrastructure.usermanagement.web;
|
package de.effigenix.infrastructure.usermanagement.web;
|
||||||
|
|
||||||
import de.effigenix.application.usermanagement.dto.SessionToken;
|
|
||||||
import de.effigenix.domain.usermanagement.RoleName;
|
import de.effigenix.domain.usermanagement.RoleName;
|
||||||
import de.effigenix.domain.usermanagement.UserStatus;
|
import de.effigenix.domain.usermanagement.UserStatus;
|
||||||
|
import de.effigenix.infrastructure.AbstractIntegrationTest;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.web.dto.LoginRequest;
|
import de.effigenix.infrastructure.usermanagement.web.dto.LoginRequest;
|
||||||
import de.effigenix.infrastructure.usermanagement.web.dto.LoginResponse;
|
import de.effigenix.infrastructure.usermanagement.web.dto.LoginResponse;
|
||||||
import de.effigenix.infrastructure.usermanagement.web.dto.RefreshTokenRequest;
|
import de.effigenix.infrastructure.usermanagement.web.dto.RefreshTokenRequest;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
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.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -39,89 +23,25 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for Authentication Controller.
|
* Integration tests for Authentication Controller.
|
||||||
*
|
|
||||||
* Tests authentication flows:
|
|
||||||
* - Login with valid/invalid credentials
|
|
||||||
* - JWT token validation
|
|
||||||
* - Logout flow
|
|
||||||
* - Refresh token flow
|
|
||||||
*
|
|
||||||
* Uses:
|
|
||||||
* - @SpringBootTest for full application context
|
|
||||||
* - @AutoConfigureMockMvc for MockMvc HTTP testing
|
|
||||||
* - @Transactional for test isolation
|
|
||||||
* - H2 in-memory database (configured in application-test.yml)
|
|
||||||
*
|
|
||||||
* Test Database:
|
|
||||||
* - H2 in-memory database
|
|
||||||
* - ddl-auto: create-drop (schema recreated for each test)
|
|
||||||
* - Each test runs in a transaction and is rolled back after completion
|
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@ActiveProfiles("test")
|
|
||||||
@Transactional
|
|
||||||
@DisplayName("Authentication Controller Integration Tests")
|
@DisplayName("Authentication Controller Integration Tests")
|
||||||
class AuthControllerIntegrationTest {
|
class AuthControllerIntegrationTest extends AbstractIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserJpaRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RoleJpaRepository roleRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PasswordEncoder passwordEncoder;
|
|
||||||
|
|
||||||
@Value("${jwt.secret}")
|
|
||||||
private String jwtSecret;
|
|
||||||
|
|
||||||
@Value("${jwt.expiration}")
|
|
||||||
private long jwtExpiration;
|
|
||||||
|
|
||||||
private String validUsername;
|
private String validUsername;
|
||||||
private String validEmail;
|
|
||||||
private String validPassword;
|
|
||||||
private String validUserId;
|
private String validUserId;
|
||||||
private String validRefreshToken;
|
private String validRefreshToken;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
validUsername = "auth.test.user";
|
validUsername = "auth.test.user";
|
||||||
validEmail = "auth.test@example.com";
|
|
||||||
validPassword = "SecurePass123";
|
|
||||||
validUserId = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
// Create test user with ADMIN role
|
RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin role");
|
||||||
RoleEntity adminRole = new RoleEntity(
|
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
RoleName.ADMIN,
|
|
||||||
Set.of(), // Empty permissions for testing
|
|
||||||
"Admin role"
|
|
||||||
);
|
|
||||||
roleRepository.save(adminRole);
|
|
||||||
|
|
||||||
UserEntity testUser = new UserEntity(
|
UserEntity testUser = createUser(validUsername, "auth.test@example.com",
|
||||||
validUserId,
|
Set.of(adminRole), "BRANCH-TEST-001");
|
||||||
validUsername,
|
validUserId = testUser.getId();
|
||||||
validEmail,
|
|
||||||
passwordEncoder.encode(validPassword),
|
|
||||||
Set.of(adminRole),
|
|
||||||
"BRANCH-TEST-001",
|
|
||||||
UserStatus.ACTIVE,
|
|
||||||
LocalDateTime.now(),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
userRepository.save(testUser);
|
|
||||||
|
|
||||||
// Create a valid refresh token
|
validRefreshToken = generateRefreshToken(validUserId, validUsername);
|
||||||
validRefreshToken = generateTestRefreshToken(validUserId, validUsername);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== LOGIN TESTS ====================
|
// ==================== LOGIN TESTS ====================
|
||||||
|
|
@ -129,7 +49,7 @@ class AuthControllerIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Login with valid credentials should return 200 with JWT tokens")
|
@DisplayName("Login with valid credentials should return 200 with JWT tokens")
|
||||||
void testLoginWithValidCredentials() throws Exception {
|
void testLoginWithValidCredentials() throws Exception {
|
||||||
LoginRequest request = new LoginRequest(validUsername, validPassword);
|
LoginRequest request = new LoginRequest(validUsername, TEST_PASSWORD);
|
||||||
|
|
||||||
MvcResult result = mockMvc.perform(post("/api/auth/login")
|
MvcResult result = mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
|
@ -154,7 +74,7 @@ class AuthControllerIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Login with invalid username should return 401 Unauthorized")
|
@DisplayName("Login with invalid username should return 401 Unauthorized")
|
||||||
void testLoginWithInvalidUsername() throws Exception {
|
void testLoginWithInvalidUsername() throws Exception {
|
||||||
LoginRequest request = new LoginRequest("non.existent.user", validPassword);
|
LoginRequest request = new LoginRequest("non.existent.user", TEST_PASSWORD);
|
||||||
|
|
||||||
mockMvc.perform(post("/api/auth/login")
|
mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
|
@ -179,12 +99,11 @@ class AuthControllerIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Login with locked user should return 401 Unauthorized")
|
@DisplayName("Login with locked user should return 401 Unauthorized")
|
||||||
void testLoginWithLockedUser() throws Exception {
|
void testLoginWithLockedUser() throws Exception {
|
||||||
// Lock the user
|
|
||||||
UserEntity user = userRepository.findByUsername(validUsername).orElseThrow();
|
UserEntity user = userRepository.findByUsername(validUsername).orElseThrow();
|
||||||
user.setStatus(UserStatus.LOCKED);
|
user.setStatus(UserStatus.LOCKED);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
|
|
||||||
LoginRequest request = new LoginRequest(validUsername, validPassword);
|
LoginRequest request = new LoginRequest(validUsername, TEST_PASSWORD);
|
||||||
|
|
||||||
mockMvc.perform(post("/api/auth/login")
|
mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
|
@ -196,12 +115,11 @@ class AuthControllerIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Login with inactive user should return 401 Unauthorized")
|
@DisplayName("Login with inactive user should return 401 Unauthorized")
|
||||||
void testLoginWithInactiveUser() throws Exception {
|
void testLoginWithInactiveUser() throws Exception {
|
||||||
// Set user to inactive
|
|
||||||
UserEntity user = userRepository.findByUsername(validUsername).orElseThrow();
|
UserEntity user = userRepository.findByUsername(validUsername).orElseThrow();
|
||||||
user.setStatus(UserStatus.INACTIVE);
|
user.setStatus(UserStatus.INACTIVE);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
|
|
||||||
LoginRequest request = new LoginRequest(validUsername, validPassword);
|
LoginRequest request = new LoginRequest(validUsername, TEST_PASSWORD);
|
||||||
|
|
||||||
mockMvc.perform(post("/api/auth/login")
|
mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
|
@ -213,7 +131,7 @@ class AuthControllerIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Login with missing username should return 400 Bad Request")
|
@DisplayName("Login with missing username should return 400 Bad Request")
|
||||||
void testLoginWithMissingUsername() throws Exception {
|
void testLoginWithMissingUsername() throws Exception {
|
||||||
LoginRequest request = new LoginRequest("", validPassword);
|
LoginRequest request = new LoginRequest("", TEST_PASSWORD);
|
||||||
|
|
||||||
mockMvc.perform(post("/api/auth/login")
|
mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
|
@ -250,7 +168,7 @@ class AuthControllerIntegrationTest {
|
||||||
@DisplayName("Logout with valid JWT token should return 204 No Content")
|
@DisplayName("Logout with valid JWT token should return 204 No Content")
|
||||||
void testLogoutWithValidToken() throws Exception {
|
void testLogoutWithValidToken() throws Exception {
|
||||||
// First login to get a valid token
|
// First login to get a valid token
|
||||||
LoginRequest loginRequest = new LoginRequest(validUsername, validPassword);
|
LoginRequest loginRequest = new LoginRequest(validUsername, TEST_PASSWORD);
|
||||||
MvcResult loginResult = mockMvc.perform(post("/api/auth/login")
|
MvcResult loginResult = mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(loginRequest)))
|
.content(objectMapper.writeValueAsString(loginRequest)))
|
||||||
|
|
@ -303,8 +221,6 @@ class AuthControllerIntegrationTest {
|
||||||
void testRefreshTokenWithValidToken() throws Exception {
|
void testRefreshTokenWithValidToken() throws Exception {
|
||||||
RefreshTokenRequest request = new RefreshTokenRequest(validRefreshToken);
|
RefreshTokenRequest request = new RefreshTokenRequest(validRefreshToken);
|
||||||
|
|
||||||
// Note: refreshSession() is not yet implemented in JwtSessionManager,
|
|
||||||
// so it throws UnsupportedOperationException which maps to 401
|
|
||||||
mockMvc.perform(post("/api/auth/refresh")
|
mockMvc.perform(post("/api/auth/refresh")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
|
@ -351,8 +267,6 @@ class AuthControllerIntegrationTest {
|
||||||
void testRefreshTokenIsPublic() throws Exception {
|
void testRefreshTokenIsPublic() throws Exception {
|
||||||
RefreshTokenRequest request = new RefreshTokenRequest(validRefreshToken);
|
RefreshTokenRequest request = new RefreshTokenRequest(validRefreshToken);
|
||||||
|
|
||||||
// Endpoint is public (permitAll) - the 401 comes from the unimplemented
|
|
||||||
// refreshSession(), not from missing JWT authentication
|
|
||||||
mockMvc.perform(post("/api/auth/refresh")
|
mockMvc.perform(post("/api/auth/refresh")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
|
@ -365,7 +279,7 @@ class AuthControllerIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("JWT token should contain valid payload information")
|
@DisplayName("JWT token should contain valid payload information")
|
||||||
void testJWTTokenContainsValidPayload() throws Exception {
|
void testJWTTokenContainsValidPayload() throws Exception {
|
||||||
LoginRequest request = new LoginRequest(validUsername, validPassword);
|
LoginRequest request = new LoginRequest(validUsername, TEST_PASSWORD);
|
||||||
|
|
||||||
MvcResult result = mockMvc.perform(post("/api/auth/login")
|
MvcResult result = mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
|
@ -385,7 +299,7 @@ class AuthControllerIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Token expiration time should match configured expiration")
|
@DisplayName("Token expiration time should match configured expiration")
|
||||||
void testTokenExpirationTime() throws Exception {
|
void testTokenExpirationTime() throws Exception {
|
||||||
LoginRequest request = new LoginRequest(validUsername, validPassword);
|
LoginRequest request = new LoginRequest(validUsername, TEST_PASSWORD);
|
||||||
|
|
||||||
MvcResult result = mockMvc.perform(post("/api/auth/login")
|
MvcResult result = mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
|
@ -405,7 +319,7 @@ class AuthControllerIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Multiple logins should return different tokens")
|
@DisplayName("Multiple logins should return different tokens")
|
||||||
void testMultipleLoginsReturnDifferentTokens() throws Exception {
|
void testMultipleLoginsReturnDifferentTokens() throws Exception {
|
||||||
LoginRequest request = new LoginRequest(validUsername, validPassword);
|
LoginRequest request = new LoginRequest(validUsername, TEST_PASSWORD);
|
||||||
|
|
||||||
// First login
|
// First login
|
||||||
MvcResult result1 = mockMvc.perform(post("/api/auth/login")
|
MvcResult result1 = mockMvc.perform(post("/api/auth/login")
|
||||||
|
|
@ -433,24 +347,4 @@ class AuthControllerIntegrationTest {
|
||||||
// Tokens should be different
|
// Tokens should be different
|
||||||
assertThat(response1.accessToken()).isNotEqualTo(response2.accessToken());
|
assertThat(response1.accessToken()).isNotEqualTo(response2.accessToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== HELPER METHODS ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a test refresh token for testing refresh endpoint.
|
|
||||||
* In production, refresh tokens are managed by SessionManager.
|
|
||||||
*/
|
|
||||||
private String generateTestRefreshToken(String userId, String username) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
|
||||||
jwtSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("type", "refresh")
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + 7200000)) // 2 hours
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,36 +2,23 @@ package de.effigenix.infrastructure.usermanagement.web;
|
||||||
|
|
||||||
import de.effigenix.domain.usermanagement.RoleName;
|
import de.effigenix.domain.usermanagement.RoleName;
|
||||||
import de.effigenix.domain.usermanagement.UserStatus;
|
import de.effigenix.domain.usermanagement.UserStatus;
|
||||||
|
import de.effigenix.infrastructure.AbstractIntegrationTest;
|
||||||
import de.effigenix.infrastructure.audit.AuditLogEntity;
|
import de.effigenix.infrastructure.audit.AuditLogEntity;
|
||||||
import de.effigenix.infrastructure.audit.AuditLogJpaRepository;
|
import de.effigenix.infrastructure.audit.AuditLogJpaRepository;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.web.dto.CreateUserRequest;
|
import de.effigenix.infrastructure.usermanagement.web.dto.CreateUserRequest;
|
||||||
import de.effigenix.infrastructure.usermanagement.web.dto.LoginRequest;
|
import de.effigenix.infrastructure.usermanagement.web.dto.LoginRequest;
|
||||||
import de.effigenix.infrastructure.usermanagement.web.dto.LoginResponse;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.web.dto.UpdateUserRequest;
|
import de.effigenix.infrastructure.usermanagement.web.dto.UpdateUserRequest;
|
||||||
import de.effigenix.application.usermanagement.AuditEvent;
|
import de.effigenix.application.usermanagement.AuditEvent;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
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.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -42,51 +29,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for Security and Authorization.
|
* Integration tests for Security and Authorization.
|
||||||
*
|
|
||||||
* Tests:
|
|
||||||
* - Authorization (ADMIN-only endpoints reject non-admin users)
|
|
||||||
* - Branch-based filtering
|
|
||||||
* - Missing/expired JWT returns 401
|
|
||||||
* - Audit logging for critical operations
|
|
||||||
* - Verify audit logs contain actor, timestamp, IP address
|
|
||||||
*
|
|
||||||
* Uses:
|
|
||||||
* - @SpringBootTest for full application context
|
|
||||||
* - @AutoConfigureMockMvc for MockMvc HTTP testing
|
|
||||||
* - @Transactional for test isolation
|
|
||||||
* - H2 in-memory database (configured in application-test.yml)
|
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@ActiveProfiles("test")
|
|
||||||
@Transactional
|
|
||||||
@DisplayName("Security and Authorization Integration Tests")
|
@DisplayName("Security and Authorization Integration Tests")
|
||||||
class SecurityIntegrationTest {
|
class SecurityIntegrationTest extends AbstractIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserJpaRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RoleJpaRepository roleRepository;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuditLogJpaRepository auditLogRepository;
|
private AuditLogJpaRepository auditLogRepository;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PasswordEncoder passwordEncoder;
|
|
||||||
|
|
||||||
@Value("${jwt.secret}")
|
|
||||||
private String jwtSecret;
|
|
||||||
|
|
||||||
@Value("${jwt.expiration}")
|
|
||||||
private long jwtExpiration;
|
|
||||||
|
|
||||||
private String adminToken;
|
private String adminToken;
|
||||||
private String regularUserToken;
|
private String regularUserToken;
|
||||||
private String expiredToken;
|
private String expiredToken;
|
||||||
|
|
@ -100,54 +49,17 @@ class SecurityIntegrationTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
// Create roles
|
adminRole = createRole(RoleName.ADMIN, "Admin role");
|
||||||
adminRole = new RoleEntity(
|
userRole = createRole(RoleName.PRODUCTION_WORKER, "Production worker role");
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
RoleName.ADMIN,
|
|
||||||
Set.of(), // Empty permissions for testing
|
|
||||||
"Admin role"
|
|
||||||
);
|
|
||||||
roleRepository.save(adminRole);
|
|
||||||
|
|
||||||
userRole = new RoleEntity(
|
UserEntity adminUser = createUser("security.admin", "admin@security.test",
|
||||||
UUID.randomUUID().toString(),
|
Set.of(adminRole), "BRANCH-ADMIN");
|
||||||
RoleName.PRODUCTION_WORKER,
|
adminUserId = adminUser.getId();
|
||||||
Set.of(), // Empty permissions for testing
|
|
||||||
"Production worker role"
|
|
||||||
);
|
|
||||||
roleRepository.save(userRole);
|
|
||||||
|
|
||||||
// Create admin user
|
UserEntity regularUser = createUser("security.user", "user@security.test",
|
||||||
adminUserId = UUID.randomUUID().toString();
|
Set.of(userRole), "BRANCH-USER");
|
||||||
UserEntity adminUser = new UserEntity(
|
regularUserId = regularUser.getId();
|
||||||
adminUserId,
|
|
||||||
"security.admin",
|
|
||||||
"admin@security.test",
|
|
||||||
passwordEncoder.encode("AdminPass123"),
|
|
||||||
Set.of(adminRole),
|
|
||||||
"BRANCH-ADMIN",
|
|
||||||
UserStatus.ACTIVE,
|
|
||||||
LocalDateTime.now(),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
userRepository.save(adminUser);
|
|
||||||
|
|
||||||
// Create regular user
|
|
||||||
regularUserId = UUID.randomUUID().toString();
|
|
||||||
UserEntity regularUser = new UserEntity(
|
|
||||||
regularUserId,
|
|
||||||
"security.user",
|
|
||||||
"user@security.test",
|
|
||||||
passwordEncoder.encode("UserPass123"),
|
|
||||||
Set.of(userRole),
|
|
||||||
"BRANCH-USER",
|
|
||||||
UserStatus.ACTIVE,
|
|
||||||
LocalDateTime.now(),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
userRepository.save(regularUser);
|
|
||||||
|
|
||||||
// Generate tokens
|
|
||||||
adminToken = generateTestJWT(adminUserId, "security.admin", true);
|
adminToken = generateTestJWT(adminUserId, "security.admin", true);
|
||||||
regularUserToken = generateTestJWT(regularUserId, "security.user", false);
|
regularUserToken = generateTestJWT(regularUserId, "security.user", false);
|
||||||
expiredToken = generateExpiredToken(adminUserId, "security.admin");
|
expiredToken = generateExpiredToken(adminUserId, "security.admin");
|
||||||
|
|
@ -283,7 +195,7 @@ class SecurityIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Login endpoint without authentication should return 401 or 200 depending on credentials")
|
@DisplayName("Login endpoint without authentication should return 401 or 200 depending on credentials")
|
||||||
void testLoginEndpointIsPublic() throws Exception {
|
void testLoginEndpointIsPublic() throws Exception {
|
||||||
LoginRequest request = new LoginRequest("security.admin", "AdminPass123");
|
LoginRequest request = new LoginRequest("security.admin", TEST_PASSWORD);
|
||||||
|
|
||||||
mockMvc.perform(post("/api/auth/login")
|
mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
|
@ -295,11 +207,8 @@ class SecurityIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Refresh token endpoint without authentication should be public")
|
@DisplayName("Refresh token endpoint without authentication should be public")
|
||||||
void testRefreshEndpointIsPublic() throws Exception {
|
void testRefreshEndpointIsPublic() throws Exception {
|
||||||
// Generate a valid refresh token
|
String refreshToken = generateRefreshToken(adminUserId, "security.admin");
|
||||||
String refreshToken = generateTestRefreshToken(adminUserId, "security.admin");
|
|
||||||
|
|
||||||
// Endpoint is public (permitAll) - the 401 comes from unimplemented
|
|
||||||
// refreshSession(), not from missing JWT authentication
|
|
||||||
mockMvc.perform(post("/api/auth/refresh")
|
mockMvc.perform(post("/api/auth/refresh")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content("{\"refreshToken\":\"" + refreshToken + "\"}"))
|
.content("{\"refreshToken\":\"" + refreshToken + "\"}"))
|
||||||
|
|
@ -312,29 +221,13 @@ class SecurityIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Users should see data filtered by their branch (if filtering is implemented)")
|
@DisplayName("Users should see data filtered by their branch (if filtering is implemented)")
|
||||||
void testBranchBasedDataVisibility() throws Exception {
|
void testBranchBasedDataVisibility() throws Exception {
|
||||||
// Create user in different branch
|
UserEntity otherBranchUser = createUser("other.branch.user", "other@branch.test",
|
||||||
String otherBranchUserId = UUID.randomUUID().toString();
|
Set.of(userRole), "BRANCH-OTHER");
|
||||||
UserEntity otherBranchUser = new UserEntity(
|
|
||||||
otherBranchUserId,
|
|
||||||
"other.branch.user",
|
|
||||||
"other@branch.test",
|
|
||||||
passwordEncoder.encode("OtherPass123"),
|
|
||||||
Set.of(userRole),
|
|
||||||
"BRANCH-OTHER",
|
|
||||||
UserStatus.ACTIVE,
|
|
||||||
LocalDateTime.now(),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
userRepository.save(otherBranchUser);
|
|
||||||
|
|
||||||
// Regular user token for BRANCH-USER
|
|
||||||
mockMvc.perform(get("/api/users")
|
mockMvc.perform(get("/api/users")
|
||||||
.header("Authorization", "Bearer " + regularUserToken))
|
.header("Authorization", "Bearer " + regularUserToken))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
||||||
// Both branches should be visible in list (if no filtering is implemented)
|
|
||||||
// This test documents the current behavior
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== AUDIT LOGGING TESTS ====================
|
// ==================== AUDIT LOGGING TESTS ====================
|
||||||
|
|
@ -342,7 +235,6 @@ class SecurityIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Create user operation should create audit log entry")
|
@DisplayName("Create user operation should create audit log entry")
|
||||||
void testCreateUserAuditLogging() throws Exception {
|
void testCreateUserAuditLogging() throws Exception {
|
||||||
// Clear existing audit logs
|
|
||||||
auditLogRepository.deleteAll();
|
auditLogRepository.deleteAll();
|
||||||
|
|
||||||
CreateUserRequest request = new CreateUserRequest(
|
CreateUserRequest request = new CreateUserRequest(
|
||||||
|
|
@ -360,7 +252,6 @@ class SecurityIntegrationTest {
|
||||||
.andExpect(status().isCreated())
|
.andExpect(status().isCreated())
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
||||||
// Verify audit log was created
|
|
||||||
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
||||||
assertThat(logs)
|
assertThat(logs)
|
||||||
.filteredOn(log -> log.getEvent() == AuditEvent.USER_CREATED)
|
.filteredOn(log -> log.getEvent() == AuditEvent.USER_CREATED)
|
||||||
|
|
@ -402,7 +293,6 @@ class SecurityIntegrationTest {
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
|
|
||||||
// Verify actor is the admin user
|
|
||||||
assertThat(auditLog.getPerformedBy()).isEqualTo(adminUserId);
|
assertThat(auditLog.getPerformedBy()).isEqualTo(adminUserId);
|
||||||
assertThat(auditLog.getPerformedBy()).isNotBlank();
|
assertThat(auditLog.getPerformedBy()).isNotBlank();
|
||||||
}
|
}
|
||||||
|
|
@ -436,7 +326,6 @@ class SecurityIntegrationTest {
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
|
|
||||||
// Verify timestamp is present and recent (within last minute)
|
|
||||||
assertThat(auditLog.getTimestamp()).isNotNull();
|
assertThat(auditLog.getTimestamp()).isNotNull();
|
||||||
assertThat(auditLog.getTimestamp()).isAfter(LocalDateTime.now().minusMinutes(1));
|
assertThat(auditLog.getTimestamp()).isAfter(LocalDateTime.now().minusMinutes(1));
|
||||||
}
|
}
|
||||||
|
|
@ -451,7 +340,6 @@ class SecurityIntegrationTest {
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
||||||
// Verify audit log was created
|
|
||||||
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
||||||
assertThat(logs)
|
assertThat(logs)
|
||||||
.filteredOn(log -> log.getEvent() == AuditEvent.USER_LOCKED)
|
.filteredOn(log -> log.getEvent() == AuditEvent.USER_LOCKED)
|
||||||
|
|
@ -471,18 +359,15 @@ class SecurityIntegrationTest {
|
||||||
void testUnlockUserAuditLogging() throws Exception {
|
void testUnlockUserAuditLogging() throws Exception {
|
||||||
auditLogRepository.deleteAll();
|
auditLogRepository.deleteAll();
|
||||||
|
|
||||||
// First lock the user
|
|
||||||
UserEntity user = userRepository.findById(regularUserId).orElseThrow();
|
UserEntity user = userRepository.findById(regularUserId).orElseThrow();
|
||||||
user.setStatus(UserStatus.LOCKED);
|
user.setStatus(UserStatus.LOCKED);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
|
|
||||||
// Then unlock
|
|
||||||
mockMvc.perform(post("/api/users/{id}/unlock", regularUserId)
|
mockMvc.perform(post("/api/users/{id}/unlock", regularUserId)
|
||||||
.header("Authorization", "Bearer " + adminToken))
|
.header("Authorization", "Bearer " + adminToken))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
||||||
// Verify audit log was created
|
|
||||||
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
||||||
assertThat(logs)
|
assertThat(logs)
|
||||||
.filteredOn(log -> log.getEvent() == AuditEvent.USER_UNLOCKED)
|
.filteredOn(log -> log.getEvent() == AuditEvent.USER_UNLOCKED)
|
||||||
|
|
@ -506,7 +391,6 @@ class SecurityIntegrationTest {
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
||||||
// Verify audit log was created
|
|
||||||
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
||||||
assertThat(logs)
|
assertThat(logs)
|
||||||
.filteredOn(log -> log.getEvent() == AuditEvent.USER_UPDATED)
|
.filteredOn(log -> log.getEvent() == AuditEvent.USER_UPDATED)
|
||||||
|
|
@ -526,7 +410,7 @@ class SecurityIntegrationTest {
|
||||||
void testLoginSuccessAuditLogging() throws Exception {
|
void testLoginSuccessAuditLogging() throws Exception {
|
||||||
auditLogRepository.deleteAll();
|
auditLogRepository.deleteAll();
|
||||||
|
|
||||||
LoginRequest request = new LoginRequest("security.admin", "AdminPass123");
|
LoginRequest request = new LoginRequest("security.admin", TEST_PASSWORD);
|
||||||
|
|
||||||
mockMvc.perform(post("/api/auth/login")
|
mockMvc.perform(post("/api/auth/login")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
|
@ -534,7 +418,6 @@ class SecurityIntegrationTest {
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
||||||
// Verify audit log was created
|
|
||||||
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
||||||
assertThat(logs)
|
assertThat(logs)
|
||||||
.filteredOn(log -> log.getEvent() == AuditEvent.LOGIN_SUCCESS)
|
.filteredOn(log -> log.getEvent() == AuditEvent.LOGIN_SUCCESS)
|
||||||
|
|
@ -554,7 +437,6 @@ class SecurityIntegrationTest {
|
||||||
.andExpect(status().isUnauthorized())
|
.andExpect(status().isUnauthorized())
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
||||||
// Verify audit log was created
|
|
||||||
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
List<AuditLogEntity> logs = auditLogRepository.findAll();
|
||||||
assertThat(logs)
|
assertThat(logs)
|
||||||
.filteredOn(log -> log.getEvent() == AuditEvent.LOGIN_FAILED)
|
.filteredOn(log -> log.getEvent() == AuditEvent.LOGIN_FAILED)
|
||||||
|
|
@ -563,58 +445,10 @@ class SecurityIntegrationTest {
|
||||||
|
|
||||||
// ==================== HELPER METHODS ====================
|
// ==================== HELPER METHODS ====================
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a test JWT token with admin permissions.
|
|
||||||
*/
|
|
||||||
private String generateTestJWT(String userId, String username, boolean isAdmin) {
|
private String generateTestJWT(String userId, String username, boolean isAdmin) {
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
String permissions = isAdmin
|
String permissions = isAdmin
|
||||||
? "USER_READ,USER_WRITE,USER_DELETE,USER_LOCK,USER_UNLOCK,ROLE_READ,ROLE_WRITE,ROLE_ASSIGN,ROLE_REMOVE"
|
? "USER_READ,USER_WRITE,USER_DELETE,USER_LOCK,USER_UNLOCK,ROLE_READ,ROLE_WRITE,ROLE_ASSIGN,ROLE_REMOVE"
|
||||||
: "USER_READ";
|
: "USER_READ";
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
return generateToken(userId, username, permissions);
|
||||||
jwtSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("permissions", permissions)
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + jwtExpiration))
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates an expired JWT token for testing expired token handling.
|
|
||||||
*/
|
|
||||||
private String generateExpiredToken(String userId, String username) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
|
||||||
jwtSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("permissions", "")
|
|
||||||
.issuedAt(new Date(now - 10000))
|
|
||||||
.expiration(new Date(now - 5000)) // Expired 5 seconds ago
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a test refresh token.
|
|
||||||
*/
|
|
||||||
private String generateTestRefreshToken(String userId, String username) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
|
||||||
jwtSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("type", "refresh")
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + 7200000))
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,18 @@
|
||||||
package de.effigenix.infrastructure.usermanagement.web;
|
package de.effigenix.infrastructure.usermanagement.web;
|
||||||
|
|
||||||
import de.effigenix.application.usermanagement.dto.UserDTO;
|
|
||||||
import de.effigenix.domain.usermanagement.RoleName;
|
import de.effigenix.domain.usermanagement.RoleName;
|
||||||
import de.effigenix.domain.usermanagement.UserStatus;
|
import de.effigenix.domain.usermanagement.UserStatus;
|
||||||
|
import de.effigenix.infrastructure.AbstractIntegrationTest;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity;
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository;
|
|
||||||
import de.effigenix.infrastructure.usermanagement.web.dto.*;
|
import de.effigenix.infrastructure.usermanagement.web.dto.*;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
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.MvcResult;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -38,49 +23,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for User Management Controller.
|
* Integration tests for User Management Controller.
|
||||||
*
|
|
||||||
* Tests user management operations:
|
|
||||||
* - Create user (success, validation errors, duplicates)
|
|
||||||
* - List users
|
|
||||||
* - Get user by ID
|
|
||||||
* - Update user
|
|
||||||
* - Lock/unlock user
|
|
||||||
* - Change password
|
|
||||||
*
|
|
||||||
* Uses:
|
|
||||||
* - @SpringBootTest for full application context
|
|
||||||
* - @AutoConfigureMockMvc for MockMvc HTTP testing
|
|
||||||
* - @Transactional for test isolation
|
|
||||||
* - H2 in-memory database (configured in application-test.yml)
|
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@ActiveProfiles("test")
|
|
||||||
@Transactional
|
|
||||||
@DisplayName("User Controller Integration Tests")
|
@DisplayName("User Controller Integration Tests")
|
||||||
class UserControllerIntegrationTest {
|
class UserControllerIntegrationTest extends AbstractIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserJpaRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RoleJpaRepository roleRepository;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PasswordEncoder passwordEncoder;
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
@Value("${jwt.secret}")
|
|
||||||
private String jwtSecret;
|
|
||||||
|
|
||||||
@Value("${jwt.expiration}")
|
|
||||||
private long jwtExpiration;
|
|
||||||
|
|
||||||
private String adminToken;
|
private String adminToken;
|
||||||
private String regularUserToken;
|
private String regularUserToken;
|
||||||
private String adminUserId;
|
private String adminUserId;
|
||||||
|
|
@ -91,54 +40,15 @@ class UserControllerIntegrationTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
// Create roles
|
adminRole = createRole(RoleName.ADMIN, "Admin role");
|
||||||
adminRole = new RoleEntity(
|
userRole = createRole(RoleName.PRODUCTION_WORKER, "Production worker role");
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
RoleName.ADMIN,
|
|
||||||
Set.of(), // Empty permissions for testing
|
|
||||||
"Admin role"
|
|
||||||
);
|
|
||||||
roleRepository.save(adminRole);
|
|
||||||
|
|
||||||
userRole = new RoleEntity(
|
UserEntity adminUser = createUser("admin.user", "admin@example.com", Set.of(adminRole), "BRANCH-001");
|
||||||
UUID.randomUUID().toString(),
|
adminUserId = adminUser.getId();
|
||||||
RoleName.PRODUCTION_WORKER,
|
|
||||||
Set.of(), // Empty permissions for testing
|
|
||||||
"Production worker role"
|
|
||||||
);
|
|
||||||
roleRepository.save(userRole);
|
|
||||||
|
|
||||||
// Create admin user
|
UserEntity regularUser = createUser("regular.user", "regular@example.com", Set.of(userRole), "BRANCH-001");
|
||||||
adminUserId = UUID.randomUUID().toString();
|
regularUserId = regularUser.getId();
|
||||||
UserEntity adminUser = new UserEntity(
|
|
||||||
adminUserId,
|
|
||||||
"admin.user",
|
|
||||||
"admin@example.com",
|
|
||||||
passwordEncoder.encode("AdminPass123"),
|
|
||||||
Set.of(adminRole),
|
|
||||||
"BRANCH-001",
|
|
||||||
UserStatus.ACTIVE,
|
|
||||||
LocalDateTime.now(),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
userRepository.save(adminUser);
|
|
||||||
|
|
||||||
// Create regular user
|
|
||||||
regularUserId = UUID.randomUUID().toString();
|
|
||||||
UserEntity regularUser = new UserEntity(
|
|
||||||
regularUserId,
|
|
||||||
"regular.user",
|
|
||||||
"regular@example.com",
|
|
||||||
passwordEncoder.encode("RegularPass123"),
|
|
||||||
Set.of(userRole),
|
|
||||||
"BRANCH-001",
|
|
||||||
UserStatus.ACTIVE,
|
|
||||||
LocalDateTime.now(),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
userRepository.save(regularUser);
|
|
||||||
|
|
||||||
// Generate JWT tokens
|
|
||||||
adminToken = generateTestJWT(adminUserId, "admin.user", true);
|
adminToken = generateTestJWT(adminUserId, "admin.user", true);
|
||||||
regularUserToken = generateTestJWT(regularUserId, "regular.user", false);
|
regularUserToken = generateTestJWT(regularUserId, "regular.user", false);
|
||||||
}
|
}
|
||||||
|
|
@ -515,7 +425,7 @@ class UserControllerIntegrationTest {
|
||||||
@DisplayName("Change password with correct current password should return 204 No Content")
|
@DisplayName("Change password with correct current password should return 204 No Content")
|
||||||
void testChangePasswordWithValidCurrentPassword() throws Exception {
|
void testChangePasswordWithValidCurrentPassword() throws Exception {
|
||||||
ChangePasswordRequest request = new ChangePasswordRequest(
|
ChangePasswordRequest request = new ChangePasswordRequest(
|
||||||
"RegularPass123", // Current password
|
TEST_PASSWORD, // Current password
|
||||||
"NewSecurePass456!"
|
"NewSecurePass456!"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -551,7 +461,7 @@ class UserControllerIntegrationTest {
|
||||||
@DisplayName("Change password with new password too short should return 400 Bad Request")
|
@DisplayName("Change password with new password too short should return 400 Bad Request")
|
||||||
void testChangePasswordWithShortNewPassword() throws Exception {
|
void testChangePasswordWithShortNewPassword() throws Exception {
|
||||||
ChangePasswordRequest request = new ChangePasswordRequest(
|
ChangePasswordRequest request = new ChangePasswordRequest(
|
||||||
"RegularPass123",
|
TEST_PASSWORD,
|
||||||
"Short1" // Less than 8 characters
|
"Short1" // Less than 8 characters
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -585,7 +495,7 @@ class UserControllerIntegrationTest {
|
||||||
@DisplayName("Change password without authentication should return 401 Unauthorized")
|
@DisplayName("Change password without authentication should return 401 Unauthorized")
|
||||||
void testChangePasswordWithoutAuthentication() throws Exception {
|
void testChangePasswordWithoutAuthentication() throws Exception {
|
||||||
ChangePasswordRequest request = new ChangePasswordRequest(
|
ChangePasswordRequest request = new ChangePasswordRequest(
|
||||||
"RegularPass123",
|
TEST_PASSWORD,
|
||||||
"NewSecurePass456!"
|
"NewSecurePass456!"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -598,23 +508,10 @@ class UserControllerIntegrationTest {
|
||||||
|
|
||||||
// ==================== HELPER METHODS ====================
|
// ==================== HELPER METHODS ====================
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a test JWT token with USER_MANAGEMENT permission for admin.
|
|
||||||
*/
|
|
||||||
private String generateTestJWT(String userId, String username, boolean isAdmin) {
|
private String generateTestJWT(String userId, String username, boolean isAdmin) {
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
javax.crypto.SecretKey key = io.jsonwebtoken.security.Keys.hmacShaKeyFor(
|
|
||||||
jwtSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
String permissions = isAdmin
|
String permissions = isAdmin
|
||||||
? "USER_READ,USER_WRITE,USER_DELETE,USER_LOCK,USER_UNLOCK,ROLE_READ,ROLE_WRITE,ROLE_ASSIGN,ROLE_REMOVE"
|
? "USER_READ,USER_WRITE,USER_DELETE,USER_LOCK,USER_UNLOCK,ROLE_READ,ROLE_WRITE,ROLE_ASSIGN,ROLE_REMOVE"
|
||||||
: "USER_READ";
|
: "USER_READ";
|
||||||
return Jwts.builder()
|
return generateToken(userId, username, permissions);
|
||||||
.subject(userId)
|
|
||||||
.claim("username", username)
|
|
||||||
.claim("permissions", permissions)
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + jwtExpiration))
|
|
||||||
.signWith(key)
|
|
||||||
.compact();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue