1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 10:29:35 +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:
Sebastian Frick 2026-02-19 21:33:44 +01:00
parent 5219c93dd1
commit 7b1c114693
9 changed files with 195 additions and 803 deletions

View file

@ -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);
}
}

View file

@ -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();
}
} }

View file

@ -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();
}
} }

View file

@ -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();
}
} }

View file

@ -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();
}
} }

View file

@ -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();
}
} }

View file

@ -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();
}
} }

View file

@ -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();
} }
} }

View file

@ -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();
} }
} }