feat(auth): remove username field, migrate identity to email
- AppUser entity: replace username with email (NOT NULL, UNIQUE, colon-pattern validated) - AppUserRepository: remove findByUsername, rename search JPQL to searchByEmailOrName (searches email + firstName + lastName) - CreateUserRequest: remove username, require email with colon guard - UserService: rename findByUsername→findByEmail, createUserOrUpdate upserts by email, blank-email guard throws instead of setting null - UserController + all other controllers: findByEmail(auth.getName()) - DataInitializer: email-based config and lookup, E2E users have email - V44 migration: pre-check + email NOT NULL + drop username column - All tests updated: .username() builders removed, mocks updated, NotificationRepositoryTest fixtures include email fields Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -31,8 +31,8 @@ import java.util.Set;
|
||||
@DependsOn("flyway")
|
||||
public class DataInitializer {
|
||||
|
||||
@Value("${app.admin.username:admin}")
|
||||
private String adminUsername;
|
||||
@Value("${app.admin.email:admin@familyarchive.local}")
|
||||
private String adminEmail;
|
||||
|
||||
@Value("${app.admin.password:admin123}")
|
||||
private String adminPassword;
|
||||
@@ -43,26 +43,23 @@ public class DataInitializer {
|
||||
@Bean
|
||||
public CommandLineRunner initAdminUser(PasswordEncoder passwordEncoder) {
|
||||
return args -> {
|
||||
if (userRepository.findByUsername(adminUsername).isEmpty()) {
|
||||
log.info("Kein Admin-User '{}' gefunden. Erstelle Default-Admin...", adminUsername);
|
||||
if (userRepository.findByEmail(adminEmail).isEmpty()) {
|
||||
log.info("Kein Admin-User '{}' gefunden. Erstelle Default-Admin...", adminEmail);
|
||||
|
||||
// 1. Admin Gruppe erstellen
|
||||
UserGroup adminGroup = UserGroup.builder()
|
||||
.name("Administrators")
|
||||
.permissions(Set.of("ADMIN", "READ_ALL", "WRITE_ALL", "ANNOTATE_ALL", "ADMIN_USER", "ADMIN_TAG", "ADMIN_PERMISSION"))
|
||||
.build();
|
||||
groupRepository.save(adminGroup);
|
||||
|
||||
// 2. Admin User erstellen
|
||||
AppUser admin = AppUser.builder()
|
||||
.username(adminUsername)
|
||||
.password(passwordEncoder.encode(adminPassword)) // Passwort verschlüsseln!
|
||||
.email("admin@familyarchive.local")
|
||||
.email(adminEmail)
|
||||
.password(passwordEncoder.encode(adminPassword))
|
||||
.groups(Set.of(adminGroup))
|
||||
.build();
|
||||
userRepository.save(admin);
|
||||
|
||||
log.info("Default Admin erstellt: User='{}'", adminUsername);
|
||||
log.info("Default Admin erstellt: Email='{}'", adminEmail);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -84,16 +81,13 @@ public class DataInitializer {
|
||||
TagRepository tagRepo,
|
||||
PasswordEncoder passwordEncoder) {
|
||||
return args -> {
|
||||
// Always reset the admin password to the configured value so a failed password-reset
|
||||
// test from a previous run can never leave the account locked out.
|
||||
userRepository.findByUsername(adminUsername).ifPresent(admin -> {
|
||||
userRepository.findByEmail(adminEmail).ifPresent(admin -> {
|
||||
admin.setPassword(passwordEncoder.encode(adminPassword));
|
||||
userRepository.save(admin);
|
||||
log.info("E2E seed: Admin-Passwort auf konfigurierten Wert zurückgesetzt.");
|
||||
});
|
||||
|
||||
// Always ensure the read-only test user exists, even when seed data was already loaded.
|
||||
if (userRepository.findByUsername("reader").isEmpty()) {
|
||||
if (userRepository.findByEmail("reader@familyarchive.local").isEmpty()) {
|
||||
log.info("E2E seed: Erstelle 'reader'-Testbenutzer...");
|
||||
UserGroup leserGroup = groupRepository.findByName("Leser").orElseGet(() ->
|
||||
groupRepository.save(UserGroup.builder()
|
||||
@@ -101,7 +95,7 @@ public class DataInitializer {
|
||||
.permissions(Set.of("READ_ALL"))
|
||||
.build()));
|
||||
userRepository.save(AppUser.builder()
|
||||
.username("reader")
|
||||
.email("reader@familyarchive.local")
|
||||
.password(passwordEncoder.encode("reader123"))
|
||||
.groups(Set.of(leserGroup))
|
||||
.build());
|
||||
@@ -131,7 +125,6 @@ public class DataInitializer {
|
||||
Tag tagUrlaub = tagRepo.save(Tag.builder().name("Urlaub").build());
|
||||
|
||||
// ── Documents ────────────────────────────────────────────────────
|
||||
// 1. Fully transcribed letter — used by search + detail E2E tests
|
||||
docRepo.save(Document.builder()
|
||||
.title("Geburtsurkunde Hans Müller")
|
||||
.originalFilename("geburtsurkunde_hans.pdf")
|
||||
@@ -144,7 +137,6 @@ public class DataInitializer {
|
||||
.transcription("Hiermit wird beurkundet, dass Hans Müller am 12. April 1923 in Berlin geboren wurde.")
|
||||
.build());
|
||||
|
||||
// 2. Letter with multiple receivers and tags — tests multi-receiver display
|
||||
docRepo.save(Document.builder()
|
||||
.title("Brief aus dem Krieg")
|
||||
.originalFilename("brief_krieg_1944.pdf")
|
||||
@@ -157,7 +149,6 @@ public class DataInitializer {
|
||||
.transcription("Liebe Anna, ich schreibe dir aus der Front. Es geht mir den Umständen entsprechend gut.")
|
||||
.build());
|
||||
|
||||
// 3. Postcard — no transcription, tests PLACEHOLDER status
|
||||
docRepo.save(Document.builder()
|
||||
.title("Urlaubspostkarte Ostsee")
|
||||
.originalFilename("postkarte_1965.jpg")
|
||||
@@ -169,7 +160,6 @@ public class DataInitializer {
|
||||
.tags(Set.of(tagUrlaub))
|
||||
.build());
|
||||
|
||||
// 4. Document with no sender — tests null-sender display ("Unbekannt")
|
||||
docRepo.save(Document.builder()
|
||||
.title("Unbekanntes Dokument")
|
||||
.originalFilename("unbekannt.pdf")
|
||||
@@ -179,7 +169,6 @@ public class DataInitializer {
|
||||
.receivers(Set.of(maria))
|
||||
.build());
|
||||
|
||||
// 5. Document with minimal metadata — tests sparse display
|
||||
docRepo.save(Document.builder()
|
||||
.title("Scan ohne Titel")
|
||||
.originalFilename("scan_ohne_titel.pdf")
|
||||
|
||||
@@ -72,7 +72,7 @@ public class AnnotationController {
|
||||
private UUID resolveUserId(Authentication authentication) {
|
||||
if (authentication == null || !authentication.isAuthenticated()) return null;
|
||||
try {
|
||||
AppUser user = userService.findByUsername(authentication.getName());
|
||||
AppUser user = userService.findByEmail(authentication.getName());
|
||||
return user != null ? user.getId() : null;
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not resolve user for annotation: {}", e.getMessage());
|
||||
|
||||
@@ -144,7 +144,7 @@ public class CommentController {
|
||||
private AppUser resolveUser(Authentication authentication) {
|
||||
if (authentication == null || !authentication.isAuthenticated()) return null;
|
||||
try {
|
||||
return userService.findByUsername(authentication.getName());
|
||||
return userService.findByEmail(authentication.getName());
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not resolve user for comment: {}", e.getMessage());
|
||||
return null;
|
||||
|
||||
@@ -100,6 +100,6 @@ public class NotificationController {
|
||||
// ─── private helpers ──────────────────────────────────────────────────────
|
||||
|
||||
private AppUser resolveUser(Authentication authentication) {
|
||||
return userService.findByUsername(authentication.getName());
|
||||
return userService.findByEmail(authentication.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ public class OcrController {
|
||||
private UUID resolveUserId(Authentication authentication) {
|
||||
if (authentication == null || !authentication.isAuthenticated()) return null;
|
||||
try {
|
||||
AppUser user = userService.findByUsername(authentication.getName());
|
||||
AppUser user = userService.findByEmail(authentication.getName());
|
||||
return user != null ? user.getId() : null;
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to resolve user ID for authentication: {}", authentication.getName(), e);
|
||||
|
||||
@@ -101,7 +101,7 @@ public class TranscriptionBlockController {
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
throw DomainException.unauthorized("Authentication required");
|
||||
}
|
||||
AppUser user = userService.findByUsername(authentication.getName());
|
||||
AppUser user = userService.findByEmail(authentication.getName());
|
||||
if (user == null) {
|
||||
throw DomainException.unauthorized("User not found");
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public class UserController {
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
||||
}
|
||||
AppUser user = userService.findByUsername(authentication.getName());
|
||||
AppUser user = userService.findByEmail(authentication.getName());
|
||||
user.setPassword(null);
|
||||
return ResponseEntity.ok(user);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public class UserController {
|
||||
@PutMapping("users/me")
|
||||
public ResponseEntity<AppUser> updateProfile(Authentication authentication,
|
||||
@RequestBody UpdateProfileDTO dto) {
|
||||
AppUser current = userService.findByUsername(authentication.getName());
|
||||
AppUser current = userService.findByEmail(authentication.getName());
|
||||
AppUser updated = userService.updateProfile(current.getId(), dto);
|
||||
updated.setPassword(null);
|
||||
return ResponseEntity.ok(updated);
|
||||
@@ -56,7 +56,7 @@ public class UserController {
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void changePassword(Authentication authentication,
|
||||
@RequestBody ChangePasswordDTO dto) {
|
||||
AppUser current = userService.findByUsername(authentication.getName());
|
||||
AppUser current = userService.findByEmail(authentication.getName());
|
||||
userService.changePassword(current.getId(), dto);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.raddatz.familienarchiv.dto;
|
||||
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
@@ -9,7 +10,8 @@ import java.util.UUID;
|
||||
|
||||
@Data
|
||||
public class CreateUserRequest {
|
||||
private String username;
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^[^:]+$", message = "Email must not contain a colon")
|
||||
private String email;
|
||||
private String initialPassword;
|
||||
private List<UUID> groupIds;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.raddatz.familienarchiv.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.*;
|
||||
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
@@ -17,7 +19,7 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users") // Tabellenname in Postgres
|
||||
@Table(name = "users")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@@ -30,26 +32,25 @@ public class AppUser {
|
||||
private UUID id;
|
||||
|
||||
@Column(unique = true, nullable = false)
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^[^:]+$", message = "Email must not contain a colon")
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String username;
|
||||
private String email;
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||
private String password; // Wird verschlüsselt gespeichert (BCrypt)
|
||||
private String password;
|
||||
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private LocalDate birthDate;
|
||||
|
||||
@Column(unique = true)
|
||||
private String email;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String contact;
|
||||
|
||||
@Builder.Default
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private boolean enabled = true; // Um User zu sperren ohne sie zu löschen
|
||||
private boolean enabled = true;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Builder.Default
|
||||
@@ -61,7 +62,6 @@ public class AppUser {
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private boolean notifyOnMention = false;
|
||||
|
||||
// Ein User kann in mehreren Gruppen sein
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
@JoinTable(name = "users_groups", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "group_id"))
|
||||
@Builder.Default
|
||||
@@ -77,26 +77,21 @@ public class AppUser {
|
||||
return false;
|
||||
}
|
||||
return this.groups.stream().anyMatch(group -> group.getPermissions().contains(permission));
|
||||
|
||||
}
|
||||
|
||||
public AppUser updateFromRequest(CreateUserRequest request, PasswordEncoder passwordEncoder, Set<UserGroup> groups) {
|
||||
if (request.getUsername() != null && !request.getUsername().isBlank()) {
|
||||
this.username = request.getUsername();
|
||||
}
|
||||
public AppUser updateFromRequest(CreateUserRequest request, PasswordEncoder passwordEncoder, Set<UserGroup> groups) {
|
||||
if (request.getEmail() != null && !request.getEmail().isBlank()) {
|
||||
this.email = request.getEmail();
|
||||
}
|
||||
|
||||
if (request.getEmail() != null && !request.getEmail().isBlank()) {
|
||||
this.email = request.getEmail();
|
||||
}
|
||||
if (request.getInitialPassword() != null && !request.getInitialPassword().isBlank()) {
|
||||
this.password = passwordEncoder.encode(request.getInitialPassword());
|
||||
}
|
||||
|
||||
if (request.getInitialPassword() != null && !request.getInitialPassword().isBlank()) {
|
||||
this.password = passwordEncoder.encode(request.getInitialPassword());
|
||||
}
|
||||
if (groups != null && !groups.isEmpty()) {
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
if (groups != null && !groups.isEmpty()) {
|
||||
this.groups = groups;
|
||||
return this;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,10 @@ import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface AppUserRepository extends JpaRepository<AppUser, UUID> {
|
||||
Optional<AppUser> findByUsername(String username);
|
||||
Optional<AppUser> findByEmail(String email);
|
||||
|
||||
@Query("SELECT u FROM AppUser u WHERE " +
|
||||
"LOWER(COALESCE(u.firstName, '') || ' ' || COALESCE(u.lastName, '')) LIKE LOWER(CONCAT('%', :q, '%')) " +
|
||||
"OR LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%'))")
|
||||
List<AppUser> searchByNameOrUsername(@Param("q") String q, Pageable pageable);
|
||||
}
|
||||
"LOWER(u.email) LIKE LOWER(CONCAT('%', :q, '%')) " +
|
||||
"OR LOWER(COALESCE(u.firstName, '') || ' ' || COALESCE(u.lastName, '')) LIKE LOWER(CONCAT('%', :q, '%'))")
|
||||
List<AppUser> searchByEmailOrName(@Param("q") String q, Pageable pageable);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public class CommentService {
|
||||
String first = author.getFirstName();
|
||||
String last = author.getLastName();
|
||||
if ((first == null || first.isBlank()) && (last == null || last.isBlank())) {
|
||||
return author.getUsername();
|
||||
return author.getEmail();
|
||||
}
|
||||
return ((first != null ? first : "") + " " + (last != null ? last : "")).strip();
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class DocumentVersionService {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return userService.findByUsername(auth.getName());
|
||||
return userService.findByEmail(auth.getName());
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not resolve editor for version snapshot: {}", e.getMessage());
|
||||
return null;
|
||||
@@ -114,7 +114,7 @@ public class DocumentVersionService {
|
||||
if (first != null && !first.isBlank() && last != null && !last.isBlank()) {
|
||||
return first + " " + last;
|
||||
}
|
||||
return user.getUsername();
|
||||
return user.getEmail();
|
||||
}
|
||||
|
||||
private String serializeSnapshot(Document doc) {
|
||||
|
||||
@@ -18,6 +18,6 @@ public class UserSearchService {
|
||||
|
||||
public List<AppUser> search(String query) {
|
||||
if (query == null || query.isBlank()) return List.of();
|
||||
return userRepository.searchByNameOrUsername(query.trim(), PageRequest.of(0, MAX_RESULTS));
|
||||
return userRepository.searchByEmailOrName(query.trim(), PageRequest.of(0, MAX_RESULTS));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,23 +36,22 @@ public class UserService {
|
||||
|
||||
@Transactional
|
||||
public AppUser createUserOrUpdate(CreateUserRequest request) {
|
||||
log.info("Creating or updating user: {}", request.getUsername());
|
||||
log.info("Creating or updating user: {}", request.getEmail());
|
||||
|
||||
Set<UserGroup> groups = new HashSet<>();
|
||||
if (request.getGroupIds() != null && !request.getGroupIds().isEmpty()) {
|
||||
groups.addAll(groupRepository.findAllById(request.getGroupIds()));
|
||||
}
|
||||
|
||||
Optional<AppUser> existingUser = userRepository.findByUsername(request.getUsername());
|
||||
Optional<AppUser> existingUser = userRepository.findByEmail(request.getEmail());
|
||||
AppUser user;
|
||||
|
||||
if (existingUser.isPresent()) {
|
||||
log.info("User exists, updating: {}", request.getUsername());
|
||||
log.info("User exists, updating: {}", request.getEmail());
|
||||
user = existingUser.get().updateFromRequest(request, passwordEncoder, groups);
|
||||
} else {
|
||||
log.info("Creating new user: {}", request.getUsername());
|
||||
log.info("Creating new user: {}", request.getEmail());
|
||||
user = AppUser.builder()
|
||||
.username(request.getUsername())
|
||||
.email(request.getEmail())
|
||||
.password(passwordEncoder.encode(request.getInitialPassword()))
|
||||
.groups(groups)
|
||||
@@ -158,9 +157,9 @@ public class UserService {
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
public AppUser findByUsername(String username) {
|
||||
return userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> DomainException.notFound(ErrorCode.USER_NOT_FOUND, "No user found for username: " + username));
|
||||
public AppUser findByEmail(String email) {
|
||||
return userRepository.findByEmail(email)
|
||||
.orElseThrow(() -> DomainException.notFound(ErrorCode.USER_NOT_FOUND, "No user found for email: " + email));
|
||||
}
|
||||
|
||||
public List<AppUser> getAllUsers() {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
-- Abort if any user has no email address set.
|
||||
-- All users must have an email before this migration can run.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM users WHERE email IS NULL) THEN
|
||||
RAISE EXCEPTION 'Migration aborted: some users have no email address. Set emails for all users before running this migration.';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
|
||||
ALTER TABLE users DROP COLUMN username;
|
||||
@@ -280,7 +280,7 @@ class AnnotationControllerTest {
|
||||
void createAnnotation_resolvesNullUserId_whenUserServiceThrows() throws Exception {
|
||||
// findByUsername throws → catch block → resolveUserId returns null
|
||||
UUID docId = UUID.randomUUID();
|
||||
when(userService.findByUsername(any())).thenThrow(new RuntimeException("DB error"));
|
||||
when(userService.findByEmail(any())).thenThrow(new RuntimeException("DB error"));
|
||||
DocumentAnnotation saved = DocumentAnnotation.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).pageNumber(1)
|
||||
.x(0.1).y(0.1).width(0.2).height(0.2).color("#ff0000").build();
|
||||
@@ -298,7 +298,7 @@ class AnnotationControllerTest {
|
||||
void createAnnotation_resolvesNullUserId_whenUserServiceReturnsNull() throws Exception {
|
||||
// findByUsername returns null → user != null = false → resolveUserId returns null
|
||||
UUID docId = UUID.randomUUID();
|
||||
when(userService.findByUsername(any())).thenReturn(null);
|
||||
when(userService.findByEmail(any())).thenReturn(null);
|
||||
DocumentAnnotation saved = DocumentAnnotation.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).pageNumber(1)
|
||||
.x(0.1).y(0.1).width(0.2).height(0.2).color("#ff0000").build();
|
||||
|
||||
@@ -270,7 +270,7 @@ class CommentControllerTest {
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void postDocumentComment_stillSucceeds_whenUserServiceThrows() throws Exception {
|
||||
// findByUsername throws → catch block in resolveUser → author null, saves anyway
|
||||
when(userService.findByUsername(any())).thenThrow(new RuntimeException("DB error"));
|
||||
when(userService.findByEmail(any())).thenThrow(new RuntimeException("DB error"));
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(DOC_ID).content("Test comment").build();
|
||||
when(commentService.postComment(any(), any(), any(), any(), any())).thenReturn(saved);
|
||||
|
||||
@@ -60,8 +60,8 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser")
|
||||
void getNotifications_returns200_whenAuthenticatedWithNoPermissions() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
when(notificationService.getNotifications(eq(USER_ID), any(), any(), any()))
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
@@ -72,12 +72,12 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void getNotifications_returns200WithList_whenAuthenticated() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
NotificationDTO dto = new NotificationDTO(
|
||||
UUID.randomUUID(), NotificationType.REPLY, UUID.randomUUID(),
|
||||
UUID.randomUUID(), null, false, LocalDateTime.now(), "Anna Smith", "Testdokument");
|
||||
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
when(notificationService.getNotifications(eq(USER_ID), any(), any(), any()))
|
||||
.thenReturn(new PageImpl<>(List.of(dto), PageRequest.of(0, 10), 1));
|
||||
|
||||
@@ -89,8 +89,8 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void getNotifications_returnsOnlyCurrentUsersNotifications() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
when(notificationService.getNotifications(eq(USER_ID), any(), any(), any()))
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
@@ -103,8 +103,8 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void getNotifications_withTypeAndReadFalse_passesFiltersToService() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
when(notificationService.getNotifications(eq(USER_ID), eq(NotificationType.MENTION), eq(false), any()))
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
@@ -148,8 +148,8 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void markAllRead_returns204_whenAuthenticated() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
|
||||
mockMvc.perform(post("/api/notifications/read-all"))
|
||||
.andExpect(status().isNoContent());
|
||||
@@ -168,10 +168,10 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void markOneRead_returns403_whenNotificationBelongsToDifferentUser() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
UUID notifId = UUID.randomUUID();
|
||||
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
org.mockito.Mockito.doThrow(
|
||||
org.raddatz.familienarchiv.exception.DomainException.forbidden("not yours"))
|
||||
.when(notificationService).markRead(notifId, USER_ID);
|
||||
@@ -198,9 +198,9 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void getPreferences_returns200_whenUserHasReadAll() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser")
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com")
|
||||
.notifyOnReply(true).notifyOnMention(false).build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
|
||||
mockMvc.perform(get("/api/users/me/notification-preferences"))
|
||||
.andExpect(status().isOk())
|
||||
@@ -211,9 +211,9 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"WRITE_ALL"})
|
||||
void getPreferences_returns200_whenUserHasWriteAll() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser")
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com")
|
||||
.notifyOnReply(false).notifyOnMention(true).build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
|
||||
mockMvc.perform(get("/api/users/me/notification-preferences"))
|
||||
.andExpect(status().isOk())
|
||||
@@ -223,9 +223,9 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"ANNOTATE_ALL"})
|
||||
void getPreferences_returns200_whenUserHasAnnotateAll() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser")
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com")
|
||||
.notifyOnReply(false).notifyOnMention(false).build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
|
||||
mockMvc.perform(get("/api/users/me/notification-preferences"))
|
||||
.andExpect(status().isOk());
|
||||
@@ -234,8 +234,8 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"WRITE_ALL"})
|
||||
void getNotifications_returns200_whenUserHasOnlyWriteAll() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
when(notificationService.getNotifications(eq(USER_ID), any(), any(), any()))
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
@@ -248,11 +248,11 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void updatePreferences_persistsBothBooleans() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser")
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com")
|
||||
.notifyOnReply(false).notifyOnMention(false).build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
|
||||
AppUser updated = AppUser.builder().id(USER_ID).username("testuser")
|
||||
AppUser updated = AppUser.builder().id(USER_ID).email("testuser@example.com")
|
||||
.notifyOnReply(true).notifyOnMention(true).build();
|
||||
when(notificationService.updatePreferences(USER_ID, true, true)).thenReturn(updated);
|
||||
|
||||
@@ -267,11 +267,11 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"WRITE_ALL"})
|
||||
void updatePreferences_returns200_whenUserHasWriteAll() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser")
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com")
|
||||
.notifyOnReply(false).notifyOnMention(false).build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
|
||||
AppUser updated = AppUser.builder().id(USER_ID).username("testuser")
|
||||
AppUser updated = AppUser.builder().id(USER_ID).email("testuser@example.com")
|
||||
.notifyOnReply(true).notifyOnMention(false).build();
|
||||
when(notificationService.updatePreferences(USER_ID, true, false)).thenReturn(updated);
|
||||
|
||||
@@ -293,8 +293,8 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void countUnread_returns200WithCount_whenAuthenticated() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
when(notificationService.countUnread(USER_ID)).thenReturn(3L);
|
||||
|
||||
mockMvc.perform(get("/api/notifications/unread-count"))
|
||||
@@ -316,8 +316,8 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void stream_returns200_whenAuthenticated() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
when(sseEmitterRegistry.register(USER_ID)).thenReturn(new org.springframework.web.servlet.mvc.method.annotation.SseEmitter());
|
||||
|
||||
mockMvc.perform(get("/api/notifications/stream")
|
||||
@@ -330,10 +330,10 @@ class NotificationControllerTest {
|
||||
@Test
|
||||
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||
void markOneRead_returns404_whenNotificationDoesNotExist() throws Exception {
|
||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||
AppUser user = AppUser.builder().id(USER_ID).email("testuser@example.com").build();
|
||||
UUID notifId = UUID.randomUUID();
|
||||
|
||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||
when(userService.findByEmail("testuser")).thenReturn(user);
|
||||
doThrow(DomainException.notFound(ErrorCode.NOTIFICATION_NOT_FOUND, "Notification not found: " + notifId))
|
||||
.when(notificationService).markRead(notifId, USER_ID);
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class TranscriptionBlockControllerTest {
|
||||
"{\"blockIds\":[\"" + UUID.randomUUID() + "\",\"" + UUID.randomUUID() + "\"]}";
|
||||
|
||||
private AppUser mockUser() {
|
||||
return AppUser.builder().id(UUID.randomUUID()).username("user").build();
|
||||
return AppUser.builder().id(UUID.randomUUID()).email("user@example.com").build();
|
||||
}
|
||||
|
||||
private TranscriptionBlock sampleBlock() {
|
||||
@@ -161,7 +161,7 @@ class TranscriptionBlockControllerTest {
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void createBlock_returns201_withSavedBlock_whenAuthorised() throws Exception {
|
||||
when(userService.findByUsername(any())).thenReturn(mockUser());
|
||||
when(userService.findByEmail(any())).thenReturn(mockUser());
|
||||
when(transcriptionService.createBlock(eq(DOC_ID), any(), any())).thenReturn(sampleBlock());
|
||||
|
||||
mockMvc.perform(post(URL_BASE)
|
||||
@@ -175,7 +175,7 @@ class TranscriptionBlockControllerTest {
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void createBlock_returns401_whenUserNotFoundInDatabase() throws Exception {
|
||||
when(userService.findByUsername(any())).thenReturn(null);
|
||||
when(userService.findByEmail(any())).thenReturn(null);
|
||||
|
||||
mockMvc.perform(post(URL_BASE)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
@@ -209,7 +209,7 @@ class TranscriptionBlockControllerTest {
|
||||
updated.setText("Neue Fassung");
|
||||
updated.setLabel("Anrede");
|
||||
|
||||
when(userService.findByUsername(any())).thenReturn(mockUser());
|
||||
when(userService.findByEmail(any())).thenReturn(mockUser());
|
||||
when(transcriptionService.updateBlock(eq(DOC_ID), eq(BLOCK_ID), any(), any()))
|
||||
.thenReturn(updated);
|
||||
|
||||
@@ -224,7 +224,7 @@ class TranscriptionBlockControllerTest {
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void updateBlock_returns404_whenBlockDoesNotExist() throws Exception {
|
||||
when(userService.findByUsername(any())).thenReturn(mockUser());
|
||||
when(userService.findByEmail(any())).thenReturn(mockUser());
|
||||
when(transcriptionService.updateBlock(any(), any(), any(), any()))
|
||||
.thenThrow(DomainException.notFound(ErrorCode.TRANSCRIPTION_BLOCK_NOT_FOUND, "not found"));
|
||||
|
||||
@@ -237,7 +237,7 @@ class TranscriptionBlockControllerTest {
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void updateBlock_returns401_whenUserNotFoundInDatabase() throws Exception {
|
||||
when(userService.findByUsername(any())).thenReturn(null);
|
||||
when(userService.findByEmail(any())).thenReturn(null);
|
||||
|
||||
mockMvc.perform(put(URL_BLOCK)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
|
||||
@@ -31,33 +31,32 @@ class UserControllerTest {
|
||||
@MockitoBean UserService userService;
|
||||
@MockitoBean CustomUserDetailsService customUserDetailsService;
|
||||
|
||||
// ─── GET /api/users/me ────────────────────────────────────────────────────
|
||||
// ─── GET /api/users/me ────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void getCurrentUser_returns401_whenUnauthenticated() throws Exception {
|
||||
// authentication == null → returns 401 (covers null/!isAuthenticated branch)
|
||||
mockMvc.perform(get("/api/users/me"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "anna")
|
||||
@WithMockUser(username = "anna@example.com")
|
||||
void getCurrentUser_returns200_whenAuthenticated() throws Exception {
|
||||
AppUser user = AppUser.builder().id(UUID.randomUUID()).username("anna").build();
|
||||
when(userService.findByUsername("anna")).thenReturn(user);
|
||||
AppUser user = AppUser.builder().id(UUID.randomUUID()).email("anna@example.com").build();
|
||||
when(userService.findByEmail("anna@example.com")).thenReturn(user);
|
||||
|
||||
mockMvc.perform(get("/api/users/me"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.username").value("anna"));
|
||||
.andExpect(jsonPath("$.email").value("anna@example.com"));
|
||||
}
|
||||
|
||||
// ─── GET /api/users/{id} ──────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "reader")
|
||||
@WithMockUser(username = "reader@example.com")
|
||||
void getUser_returns403_whenCallerLacksAdminUserPermission() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser target = AppUser.builder().id(id).username("target").build();
|
||||
AppUser target = AppUser.builder().id(id).email("target@example.com").build();
|
||||
when(userService.getById(id)).thenReturn(target);
|
||||
|
||||
mockMvc.perform(get("/api/users/" + id))
|
||||
@@ -65,14 +64,14 @@ class UserControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin", authorities = {"ADMIN_USER"})
|
||||
@WithMockUser(username = "admin@example.com", authorities = {"ADMIN_USER"})
|
||||
void getUser_returns200_whenCallerHasAdminUserPermission() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("target").build();
|
||||
AppUser user = AppUser.builder().id(id).email("target@example.com").build();
|
||||
when(userService.getById(id)).thenReturn(user);
|
||||
|
||||
mockMvc.perform(get("/api/users/" + id))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.username").value("target"));
|
||||
.andExpect(jsonPath("$.email").value("target@example.com"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class UserSearchControllerTest {
|
||||
@WithMockUser(authorities = {"READ_ALL"})
|
||||
void search_returns200_whenAuthenticated() throws Exception {
|
||||
AppUser user = AppUser.builder().id(UUID.randomUUID())
|
||||
.firstName("Hans").lastName("Mueller").username("hans").build();
|
||||
.firstName("Hans").lastName("Mueller").email("hans@example.com").build();
|
||||
when(userSearchService.search("Hans")).thenReturn(List.of(user));
|
||||
|
||||
mockMvc.perform(get("/api/users/search").param("q", "Hans"))
|
||||
@@ -83,7 +83,7 @@ class UserSearchControllerTest {
|
||||
void search_returnsAtMostTenResults() throws Exception {
|
||||
List<AppUser> elevenUsers = IntStream.range(0, 11)
|
||||
.mapToObj(i -> AppUser.builder().id(UUID.randomUUID())
|
||||
.firstName("User").lastName(String.valueOf(i)).username("u" + i).build())
|
||||
.firstName("User").lastName(String.valueOf(i)).email("u" + i + "@example.com").build())
|
||||
.toList();
|
||||
when(userSearchService.search(anyString())).thenReturn(elevenUsers.subList(0, 10));
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ class NotificationRepositoryTest {
|
||||
void setUp() {
|
||||
notificationRepository.deleteAll();
|
||||
appUserRepository.deleteAll();
|
||||
userA = appUserRepository.save(AppUser.builder().username("userA").password("pw").build());
|
||||
userB = appUserRepository.save(AppUser.builder().username("userB").password("pw").build());
|
||||
userA = appUserRepository.save(AppUser.builder().email("userA@example.com").password("pw").build());
|
||||
userB = appUserRepository.save(AppUser.builder().email("userB@example.com").password("pw").build());
|
||||
}
|
||||
|
||||
// ─── findByRecipientIdAndTypeAndReadFalse ─────────────────────────────────
|
||||
|
||||
@@ -43,7 +43,7 @@ class CommentServiceTest {
|
||||
void postComment_capturesAuthorNameAtWriteTime() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder()
|
||||
.id(UUID.randomUUID()).username("hans").firstName("Hans").lastName("Müller").build();
|
||||
.id(UUID.randomUUID()).email("hans@example.com").firstName("Hans").lastName("Müller").build();
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("Hans Müller").content("Test").build();
|
||||
when(commentRepository.save(any())).thenReturn(saved);
|
||||
@@ -56,7 +56,7 @@ class CommentServiceTest {
|
||||
@Test
|
||||
void postComment_fallsBackToUsername_whenNamesAreBlank() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("hans42").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("hans42@example.com").build();
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("hans42").content("Test").build();
|
||||
when(commentRepository.save(any())).thenReturn(saved);
|
||||
@@ -70,8 +70,8 @@ class CommentServiceTest {
|
||||
void postComment_triggersNotifyMentions_whenMentionedUserIdsProvided() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID mentionedId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("hans").firstName("Hans").lastName("M").build();
|
||||
AppUser mentioned = AppUser.builder().id(mentionedId).username("anna").firstName("Anna").lastName("S").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("hans@example.com").firstName("Hans").lastName("M").build();
|
||||
AppUser mentioned = AppUser.builder().id(mentionedId).email("anna@example.com").firstName("Anna").lastName("S").build();
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("Hans M").content("Hey @Anna S").build();
|
||||
|
||||
@@ -89,7 +89,7 @@ class CommentServiceTest {
|
||||
void replyToComment_throwsNotFound_whenTargetCommentMissing() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID commentId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("anna").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("anna@example.com").build();
|
||||
when(commentRepository.findById(commentId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> commentService.replyToComment(docId, commentId, "Reply", List.of(), author))
|
||||
@@ -104,7 +104,7 @@ class CommentServiceTest {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID rootId = UUID.randomUUID();
|
||||
UUID replyId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("anna").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("anna@example.com").build();
|
||||
|
||||
DocumentComment root = DocumentComment.builder()
|
||||
.id(rootId).documentId(docId).parentId(null).content("Root").authorName("Hans").build();
|
||||
@@ -127,7 +127,7 @@ class CommentServiceTest {
|
||||
void replyToComment_usesDirectComment_whenReplyingToTopLevel() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID rootId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("anna").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("anna@example.com").build();
|
||||
|
||||
DocumentComment root = DocumentComment.builder()
|
||||
.id(rootId).documentId(docId).parentId(null).content("Root").authorName("Hans").build();
|
||||
@@ -147,7 +147,7 @@ class CommentServiceTest {
|
||||
void replyToComment_triggersNotifyReply_afterSave() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID rootId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("anna").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("anna@example.com").build();
|
||||
|
||||
DocumentComment root = DocumentComment.builder()
|
||||
.id(rootId).documentId(docId).parentId(null).content("Root").authorName("Hans").build();
|
||||
@@ -168,8 +168,8 @@ class CommentServiceTest {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID rootId = UUID.randomUUID();
|
||||
UUID mentionedId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("anna").build();
|
||||
AppUser mentioned = AppUser.builder().id(mentionedId).username("bob").firstName("Bob").lastName("J").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("anna@example.com").build();
|
||||
AppUser mentioned = AppUser.builder().id(mentionedId).email("bob@example.com").firstName("Bob").lastName("J").build();
|
||||
|
||||
DocumentComment root = DocumentComment.builder()
|
||||
.id(rootId).documentId(docId).parentId(null).content("Root").authorName("Hans").build();
|
||||
@@ -193,7 +193,7 @@ class CommentServiceTest {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID commentId = UUID.randomUUID();
|
||||
UUID ownerId = UUID.randomUUID();
|
||||
AppUser other = AppUser.builder().id(UUID.randomUUID()).username("other").build();
|
||||
AppUser other = AppUser.builder().id(UUID.randomUUID()).email("other@example.com").build();
|
||||
|
||||
DocumentComment comment = DocumentComment.builder()
|
||||
.id(commentId).documentId(docId).authorId(ownerId).content("Original").authorName("Hans").build();
|
||||
@@ -211,7 +211,7 @@ class CommentServiceTest {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID commentId = UUID.randomUUID();
|
||||
UUID authorId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(authorId).username("hans").build();
|
||||
AppUser author = AppUser.builder().id(authorId).email("hans@example.com").build();
|
||||
LocalDateTime created = LocalDateTime.now().minusMinutes(5);
|
||||
|
||||
DocumentComment comment = DocumentComment.builder()
|
||||
@@ -233,7 +233,7 @@ class CommentServiceTest {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID commentId = UUID.randomUUID();
|
||||
UUID ownerId = UUID.randomUUID();
|
||||
AppUser other = AppUser.builder().id(UUID.randomUUID()).username("other").build();
|
||||
AppUser other = AppUser.builder().id(UUID.randomUUID()).email("other@example.com").build();
|
||||
|
||||
DocumentComment comment = DocumentComment.builder()
|
||||
.id(commentId).documentId(docId).authorId(ownerId).authorName("Hans").content("X").build();
|
||||
@@ -251,7 +251,7 @@ class CommentServiceTest {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID commentId = UUID.randomUUID();
|
||||
UUID authorId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(authorId).username("hans").build();
|
||||
AppUser author = AppUser.builder().id(authorId).email("hans@example.com").build();
|
||||
|
||||
DocumentComment comment = DocumentComment.builder()
|
||||
.id(commentId).documentId(docId).authorId(authorId).authorName("Hans").content("X").build();
|
||||
@@ -306,7 +306,7 @@ class CommentServiceTest {
|
||||
void replyToComment_handlesNullAuthorId_inExistingReply() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID rootId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("anna").firstName("Anna").lastName("S").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("anna@example.com").firstName("Anna").lastName("S").build();
|
||||
|
||||
DocumentComment root = DocumentComment.builder()
|
||||
.id(rootId).documentId(docId).parentId(null).authorId(UUID.randomUUID()).content("Root").authorName("Root").build();
|
||||
@@ -331,7 +331,7 @@ class CommentServiceTest {
|
||||
@Test
|
||||
void postComment_fallsBackToUsername_whenFirstNameBlankAndLastNameNull() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("user42")
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("user42@example.com")
|
||||
.firstName(" ").lastName(null).build();
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("user42").content("Hi").build();
|
||||
@@ -345,7 +345,7 @@ class CommentServiceTest {
|
||||
@Test
|
||||
void postComment_fallsBackToUsername_whenFirstNameNullAndLastNameBlank() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("user42")
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("user42@example.com")
|
||||
.firstName(null).lastName(" ").build();
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("user42").content("Hi").build();
|
||||
@@ -359,7 +359,7 @@ class CommentServiceTest {
|
||||
@Test
|
||||
void postComment_includesOnlyFirstName_whenLastNameIsNull() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("user42")
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("user42@example.com")
|
||||
.firstName("Hans").lastName(null).build();
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("Hans").content("Hi").build();
|
||||
@@ -374,7 +374,7 @@ class CommentServiceTest {
|
||||
@Test
|
||||
void postComment_includesOnlyLastName_whenFirstNameIsNull() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("user42")
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("user42@example.com")
|
||||
.firstName(null).lastName("Müller").build();
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("Müller").content("Hi").build();
|
||||
@@ -391,7 +391,7 @@ class CommentServiceTest {
|
||||
@Test
|
||||
void postComment_doesNotCallUserService_whenMentionedUserIdsIsNull() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("hans")
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("hans@example.com")
|
||||
.firstName("Hans").lastName("M").build();
|
||||
DocumentComment saved = DocumentComment.builder()
|
||||
.id(UUID.randomUUID()).documentId(docId).authorName("Hans M").content("Hi").build();
|
||||
@@ -409,7 +409,7 @@ class CommentServiceTest {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID rootId = UUID.randomUUID();
|
||||
UUID existingReplyAuthorId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("anna").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("anna@example.com").build();
|
||||
|
||||
DocumentComment root = DocumentComment.builder()
|
||||
.id(rootId).documentId(docId).parentId(null).authorId(UUID.randomUUID())
|
||||
@@ -437,7 +437,7 @@ class CommentServiceTest {
|
||||
void replyToComment_excludesNullAuthorIds_fromParticipantSet() {
|
||||
UUID docId = UUID.randomUUID();
|
||||
UUID rootId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("anna").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("anna@example.com").build();
|
||||
|
||||
// Root with null authorId
|
||||
DocumentComment root = DocumentComment.builder()
|
||||
@@ -480,7 +480,7 @@ class CommentServiceTest {
|
||||
private AppUser buildAdmin() {
|
||||
return AppUser.builder()
|
||||
.id(UUID.randomUUID())
|
||||
.username("admin")
|
||||
.email("admin@example.com")
|
||||
.groups(Set.of(UserGroup.builder()
|
||||
.id(UUID.randomUUID())
|
||||
.name("admins")
|
||||
@@ -510,7 +510,7 @@ class CommentServiceTest {
|
||||
void postBlockComment_setsBlockIdOnComment() {
|
||||
UUID documentId = UUID.randomUUID();
|
||||
UUID blockId = UUID.randomUUID();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).username("felix").firstName("Felix").lastName("Brandt").build();
|
||||
AppUser author = AppUser.builder().id(UUID.randomUUID()).email("felix@example.com").firstName("Felix").lastName("Brandt").build();
|
||||
when(commentRepository.save(any())).thenAnswer(inv -> {
|
||||
DocumentComment c = inv.getArgument(0);
|
||||
c.setId(UUID.randomUUID());
|
||||
|
||||
@@ -53,8 +53,8 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_usesFirstAndLastName_whenBothPresent() {
|
||||
authenticateAs("emma");
|
||||
when(userService.findByUsername("emma")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).username("emma")
|
||||
when(userService.findByEmail("emma")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).email("emma@example.com")
|
||||
.firstName("Emma").lastName("Müller").build());
|
||||
when(versionRepository.findByDocumentIdOrderBySavedAtAsc(any())).thenReturn(List.of());
|
||||
when(versionRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
@@ -67,10 +67,10 @@ class DocumentVersionServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordVersion_usesUsername_whenNamesAreBlank() {
|
||||
void recordVersion_usesEmail_whenNamesAreBlank() {
|
||||
authenticateAs("otto99");
|
||||
when(userService.findByUsername("otto99")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).username("otto99")
|
||||
when(userService.findByEmail("otto99")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).email("otto99@example.com")
|
||||
.firstName(null).lastName(null).build());
|
||||
when(versionRepository.findByDocumentIdOrderBySavedAtAsc(any())).thenReturn(List.of());
|
||||
when(versionRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
@@ -79,7 +79,7 @@ class DocumentVersionServiceTest {
|
||||
|
||||
ArgumentCaptor<DocumentVersion> captor = ArgumentCaptor.forClass(DocumentVersion.class);
|
||||
verify(versionRepository).save(captor.capture());
|
||||
assertThat(captor.getValue().getEditorName()).isEqualTo("otto99");
|
||||
assertThat(captor.getValue().getEditorName()).isEqualTo("otto99@example.com");
|
||||
}
|
||||
|
||||
// ─── recordVersion — snapshot ─────────────────────────────────────────────
|
||||
@@ -87,7 +87,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_savesSnapshotContainingTitle() {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
when(versionRepository.findByDocumentIdOrderBySavedAtAsc(any())).thenReturn(List.of());
|
||||
when(versionRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -110,7 +110,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_changedFieldsIsEmpty_forFirstVersion() {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
when(versionRepository.findByDocumentIdOrderBySavedAtAsc(any())).thenReturn(List.of());
|
||||
when(versionRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -124,7 +124,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_includesTitleInChangedFields_whenTitleChanged() throws Exception {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
Document oldDoc = Document.builder().id(UUID.randomUUID()).title("Alt").build();
|
||||
@@ -154,7 +154,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_doesNotIncludeUnchangedFields_inChangedFields() throws Exception {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
UUID docId = UUID.randomUUID();
|
||||
@@ -181,7 +181,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_tracksSenderChange() throws Exception {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
UUID docId = UUID.randomUUID();
|
||||
@@ -209,7 +209,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_tracksReceiverChange() throws Exception {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
UUID docId = UUID.randomUUID();
|
||||
@@ -236,7 +236,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_tracksTagChange() throws Exception {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
UUID docId = UUID.randomUUID();
|
||||
@@ -410,7 +410,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_usesUnknown_whenUserServiceThrows() {
|
||||
authenticateAs("missinguser");
|
||||
when(userService.findByUsername("missinguser")).thenThrow(new RuntimeException("not found"));
|
||||
when(userService.findByEmail("missinguser")).thenThrow(new RuntimeException("not found"));
|
||||
when(versionRepository.findByDocumentIdOrderBySavedAtAsc(any())).thenReturn(List.of());
|
||||
when(versionRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -424,10 +424,10 @@ class DocumentVersionServiceTest {
|
||||
// ─── recordVersion — buildEditorName edge cases ───────────────────────────
|
||||
|
||||
@Test
|
||||
void recordVersion_usesUsername_whenFirstNameIsNotBlankButLastNameIsNull() {
|
||||
void recordVersion_usesEmail_whenFirstNameIsNotBlankButLastNameIsNull() {
|
||||
authenticateAs("user42");
|
||||
when(userService.findByUsername("user42")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).username("user42")
|
||||
when(userService.findByEmail("user42")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).email("user42@example.com")
|
||||
.firstName("Hans").lastName(null).build());
|
||||
when(versionRepository.findByDocumentIdOrderBySavedAtAsc(any())).thenReturn(List.of());
|
||||
when(versionRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
@@ -436,14 +436,14 @@ class DocumentVersionServiceTest {
|
||||
|
||||
ArgumentCaptor<DocumentVersion> captor = ArgumentCaptor.forClass(DocumentVersion.class);
|
||||
verify(versionRepository).save(captor.capture());
|
||||
assertThat(captor.getValue().getEditorName()).isEqualTo("user42");
|
||||
assertThat(captor.getValue().getEditorName()).isEqualTo("user42@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordVersion_usesUsername_whenFirstNameIsBlankButLastNameIsPresent() {
|
||||
void recordVersion_usesEmail_whenFirstNameIsBlankButLastNameIsPresent() {
|
||||
authenticateAs("user42");
|
||||
when(userService.findByUsername("user42")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).username("user42")
|
||||
when(userService.findByEmail("user42")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).email("user42@example.com")
|
||||
.firstName(" ").lastName("Müller").build());
|
||||
when(versionRepository.findByDocumentIdOrderBySavedAtAsc(any())).thenReturn(List.of());
|
||||
when(versionRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
@@ -452,14 +452,14 @@ class DocumentVersionServiceTest {
|
||||
|
||||
ArgumentCaptor<DocumentVersion> captor = ArgumentCaptor.forClass(DocumentVersion.class);
|
||||
verify(versionRepository).save(captor.capture());
|
||||
assertThat(captor.getValue().getEditorName()).isEqualTo("user42");
|
||||
assertThat(captor.getValue().getEditorName()).isEqualTo("user42@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordVersion_usesUsername_whenLastNameIsBlankButFirstNameIsPresent() {
|
||||
void recordVersion_usesEmail_whenLastNameIsBlankButFirstNameIsPresent() {
|
||||
authenticateAs("user42");
|
||||
when(userService.findByUsername("user42")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).username("user42")
|
||||
when(userService.findByEmail("user42")).thenReturn(
|
||||
AppUser.builder().id(UUID.randomUUID()).email("user42@example.com")
|
||||
.firstName("Hans").lastName(" ").build());
|
||||
when(versionRepository.findByDocumentIdOrderBySavedAtAsc(any())).thenReturn(List.of());
|
||||
when(versionRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
@@ -468,7 +468,7 @@ class DocumentVersionServiceTest {
|
||||
|
||||
ArgumentCaptor<DocumentVersion> captor = ArgumentCaptor.forClass(DocumentVersion.class);
|
||||
verify(versionRepository).save(captor.capture());
|
||||
assertThat(captor.getValue().getEditorName()).isEqualTo("user42");
|
||||
assertThat(captor.getValue().getEditorName()).isEqualTo("user42@example.com");
|
||||
}
|
||||
|
||||
// ─── recordVersion — computeChangedFields with corrupt snapshot ──────────
|
||||
@@ -476,7 +476,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_returnsEmptyChangedFields_whenPreviousSnapshotIsInvalidJson() {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
UUID docId = UUID.randomUUID();
|
||||
DocumentVersion previous = DocumentVersion.builder()
|
||||
@@ -499,7 +499,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_tracksSenderAdded_whenPreviousHadNoSender() throws Exception {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
UUID docId = UUID.randomUUID();
|
||||
@@ -525,7 +525,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_tracksReceiversAdded_whenPreviousHadNone() throws Exception {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
UUID docId = UUID.randomUUID();
|
||||
@@ -551,7 +551,7 @@ class DocumentVersionServiceTest {
|
||||
@Test
|
||||
void recordVersion_tracksTagsAdded_whenPreviousHadNone() throws Exception {
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
UUID docId = UUID.randomUUID();
|
||||
@@ -580,7 +580,7 @@ class DocumentVersionServiceTest {
|
||||
void recordVersion_senderChangedToPresent_whenPreviousSenderHasNullId() throws Exception {
|
||||
// Covers: prevSender instanceof Map = true, but id == null → prevId = null
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
UUID docId = UUID.randomUUID();
|
||||
// Manually craft a JSON where sender object exists but id is null
|
||||
@@ -610,7 +610,7 @@ class DocumentVersionServiceTest {
|
||||
void recordVersion_doesNotTrackSender_whenSenderUnchanged() throws Exception {
|
||||
// Covers: !Objects.equals(currentId, prevId) = false → don't add "sender"
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
UUID docId = UUID.randomUUID();
|
||||
@@ -641,7 +641,7 @@ class DocumentVersionServiceTest {
|
||||
void recordVersion_tracksDocumentDate_whenCurrentDocHasNonNullDate() throws Exception {
|
||||
// current.getDocumentDate() != null = true → ternary true branch in computeChangedFields
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
UUID docId = UUID.randomUUID();
|
||||
@@ -671,7 +671,7 @@ class DocumentVersionServiceTest {
|
||||
void recordVersion_tracksReceivers_whenPreviousSnapshotHasNullReceivers() throws Exception {
|
||||
// prevReceivers NOT instanceof List<?> → prevIds = Set.of() → if currentIds differ → added
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
UUID docId = UUID.randomUUID();
|
||||
// Craft snapshot where "receivers" is JSON null → deserialized as null, NOT a List
|
||||
@@ -697,7 +697,7 @@ class DocumentVersionServiceTest {
|
||||
void recordVersion_tracksTags_whenPreviousSnapshotHasNullTags() throws Exception {
|
||||
// prevTags NOT instanceof List<?> → prevNames = Set.of() → if currentNames differ → added
|
||||
authenticateAs("user1");
|
||||
when(userService.findByUsername("user1")).thenReturn(stubUser("user1"));
|
||||
when(userService.findByEmail("user1")).thenReturn(stubUser("user1"));
|
||||
|
||||
UUID docId = UUID.randomUUID();
|
||||
// Craft snapshot where "tags" is JSON null → deserialized as null, NOT a List
|
||||
@@ -741,8 +741,8 @@ class DocumentVersionServiceTest {
|
||||
new UsernamePasswordAuthenticationToken(username, null, List.of()));
|
||||
}
|
||||
|
||||
private AppUser stubUser(String username) {
|
||||
return AppUser.builder().id(UUID.randomUUID()).username(username)
|
||||
private AppUser stubUser(String email) {
|
||||
return AppUser.builder().id(UUID.randomUUID()).email(email)
|
||||
.firstName(null).lastName(null).build();
|
||||
}
|
||||
|
||||
|
||||
@@ -50,13 +50,13 @@ class NotificationServiceTest {
|
||||
void setUp() {
|
||||
notificationService = new NotificationService(notificationRepository, userService, documentService, Optional.of(mailSender), sseEmitterRegistry);
|
||||
|
||||
userA = AppUser.builder().id(UUID.randomUUID()).username("userA")
|
||||
userA = AppUser.builder().id(UUID.randomUUID()).email("userA@example.com")
|
||||
.firstName("Anna").lastName("Smith").email("a@test.com")
|
||||
.notifyOnReply(false).notifyOnMention(false).build();
|
||||
userB = AppUser.builder().id(UUID.randomUUID()).username("userB")
|
||||
userB = AppUser.builder().id(UUID.randomUUID()).email("userB@example.com")
|
||||
.firstName("Bob").lastName("Jones").email("b@test.com")
|
||||
.notifyOnReply(false).notifyOnMention(false).build();
|
||||
userC = AppUser.builder().id(UUID.randomUUID()).username("userC")
|
||||
userC = AppUser.builder().id(UUID.randomUUID()).email("userC@example.com")
|
||||
.firstName("Clara").lastName("Doe").email("c@test.com")
|
||||
.notifyOnReply(false).notifyOnMention(false).build();
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class PasswordResetServiceTest {
|
||||
private AppUser makeUser(String email) {
|
||||
return AppUser.builder()
|
||||
.id(UUID.randomUUID())
|
||||
.username("testuser")
|
||||
.email("testuser@example.com")
|
||||
.email(email)
|
||||
.password("hashed")
|
||||
.build();
|
||||
|
||||
@@ -32,7 +32,7 @@ class UserSearchServiceTest {
|
||||
List<AppUser> result = userSearchService.search(null);
|
||||
|
||||
assertThat(result).isEmpty();
|
||||
verify(userRepository, never()).searchByNameOrUsername(any(), any());
|
||||
verify(userRepository, never()).searchByEmailOrName(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -40,28 +40,28 @@ class UserSearchServiceTest {
|
||||
List<AppUser> result = userSearchService.search(" ");
|
||||
|
||||
assertThat(result).isEmpty();
|
||||
verify(userRepository, never()).searchByNameOrUsername(any(), any());
|
||||
verify(userRepository, never()).searchByEmailOrName(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void search_delegatesToRepository_whenQueryIsNonBlank() {
|
||||
AppUser user = AppUser.builder().id(UUID.randomUUID()).username("hans").build();
|
||||
when(userRepository.searchByNameOrUsername(eq("hans"), any(PageRequest.class)))
|
||||
AppUser user = AppUser.builder().id(UUID.randomUUID()).email("hans@example.com").build();
|
||||
when(userRepository.searchByEmailOrName(eq("hans"), any(PageRequest.class)))
|
||||
.thenReturn(List.of(user));
|
||||
|
||||
List<AppUser> result = userSearchService.search("hans");
|
||||
|
||||
assertThat(result).containsExactly(user);
|
||||
verify(userRepository).searchByNameOrUsername(eq("hans"), any(PageRequest.class));
|
||||
verify(userRepository).searchByEmailOrName(eq("hans"), any(PageRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void search_trimsQuery_beforeDelegating() {
|
||||
when(userRepository.searchByNameOrUsername(eq("hans"), any(PageRequest.class)))
|
||||
when(userRepository.searchByEmailOrName(eq("hans"), any(PageRequest.class)))
|
||||
.thenReturn(List.of());
|
||||
|
||||
userSearchService.search(" hans ");
|
||||
|
||||
verify(userRepository).searchByNameOrUsername(eq("hans"), any(PageRequest.class));
|
||||
verify(userRepository).searchByEmailOrName(eq("hans"), any(PageRequest.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,22 +35,22 @@ class UserServiceTest {
|
||||
@Mock PasswordEncoder passwordEncoder;
|
||||
@InjectMocks UserService userService;
|
||||
|
||||
// ─── findByUsername ───────────────────────────────────────────────────────
|
||||
// ─── findByEmail ──────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void findByUsername_throwsNotFound_whenMissing() {
|
||||
when(userRepository.findByUsername("ghost")).thenReturn(Optional.empty());
|
||||
void findByEmail_throwsNotFound_whenMissing() {
|
||||
when(userRepository.findByEmail("ghost@example.com")).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> userService.findByUsername("ghost"))
|
||||
assertThatThrownBy(() -> userService.findByEmail("ghost@example.com"))
|
||||
.isInstanceOf(DomainException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByUsername_returnsUser_whenFound() {
|
||||
AppUser user = AppUser.builder().id(UUID.randomUUID()).username("admin").build();
|
||||
when(userRepository.findByUsername("admin")).thenReturn(Optional.of(user));
|
||||
void findByEmail_returnsUser_whenFound() {
|
||||
AppUser user = AppUser.builder().id(UUID.randomUUID()).email("admin@example.com").build();
|
||||
when(userRepository.findByEmail("admin@example.com")).thenReturn(Optional.of(user));
|
||||
|
||||
assertThat(userService.findByUsername("admin")).isEqualTo(user);
|
||||
assertThat(userService.findByEmail("admin@example.com")).isEqualTo(user);
|
||||
}
|
||||
|
||||
// ─── deleteUser ───────────────────────────────────────────────────────────
|
||||
@@ -67,7 +67,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void deleteUser_deletesUser_whenFound() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("gast").build();
|
||||
AppUser user = AppUser.builder().id(id).email("gast@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
|
||||
userService.deleteUser(id);
|
||||
@@ -80,14 +80,13 @@ class UserServiceTest {
|
||||
@Test
|
||||
void createUserOrUpdate_createsNewUser_whenNotExists() {
|
||||
CreateUserRequest req = new CreateUserRequest();
|
||||
req.setUsername("newuser");
|
||||
req.setEmail("new@example.com");
|
||||
req.setInitialPassword("secret");
|
||||
req.setGroupIds(List.of());
|
||||
|
||||
when(userRepository.findByUsername("newuser")).thenReturn(Optional.empty());
|
||||
when(userRepository.findByEmail("new@example.com")).thenReturn(Optional.empty());
|
||||
when(passwordEncoder.encode("secret")).thenReturn("encoded");
|
||||
AppUser saved = AppUser.builder().id(UUID.randomUUID()).username("newuser").build();
|
||||
AppUser saved = AppUser.builder().id(UUID.randomUUID()).email("new@example.com").build();
|
||||
when(userRepository.save(any())).thenReturn(saved);
|
||||
|
||||
AppUser result = userService.createUserOrUpdate(req);
|
||||
@@ -99,19 +98,17 @@ class UserServiceTest {
|
||||
@Test
|
||||
void createUserOrUpdate_updatesExistingUser_whenFound() {
|
||||
CreateUserRequest req = new CreateUserRequest();
|
||||
req.setUsername("existing");
|
||||
req.setEmail("existing@example.com");
|
||||
req.setInitialPassword("newpass");
|
||||
req.setGroupIds(List.of());
|
||||
|
||||
AppUser existing = AppUser.builder().id(UUID.randomUUID()).username("existing").build();
|
||||
when(userRepository.findByUsername("existing")).thenReturn(Optional.of(existing));
|
||||
AppUser existing = AppUser.builder().id(UUID.randomUUID()).email("existing@example.com").build();
|
||||
when(userRepository.findByEmail("existing@example.com")).thenReturn(Optional.of(existing));
|
||||
when(passwordEncoder.encode(any())).thenReturn("encoded");
|
||||
when(userRepository.save(any())).thenReturn(existing);
|
||||
|
||||
userService.createUserOrUpdate(req);
|
||||
|
||||
// save called once with the updated existing user (no new user created)
|
||||
verify(userRepository, times(1)).save(existing);
|
||||
}
|
||||
|
||||
@@ -129,7 +126,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void getById_returnsUser_whenFound() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
|
||||
assertThat(userService.getById(id)).isEqualTo(user);
|
||||
@@ -140,7 +137,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void updateProfile_updatesFields() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.findByEmail("max@example.com")).thenReturn(Optional.empty());
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
@@ -158,8 +155,8 @@ class UserServiceTest {
|
||||
void updateProfile_throwsConflict_whenEmailTakenByAnotherUser() {
|
||||
UUID id = UUID.randomUUID();
|
||||
UUID otherId = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").build();
|
||||
AppUser other = AppUser.builder().id(otherId).username("anna").email("taken@example.com").build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").build();
|
||||
AppUser other = AppUser.builder().id(otherId).email("taken@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.findByEmail("taken@example.com")).thenReturn(Optional.of(other));
|
||||
|
||||
@@ -174,7 +171,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void updateProfile_allowsSameEmailForSameUser() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").email("max@example.com").build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.findByEmail("max@example.com")).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
@@ -191,7 +188,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void changePassword_throwsBadRequest_whenCurrentPasswordWrong() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").password("hashed").build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").password("hashed").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(passwordEncoder.matches("wrong", "hashed")).thenReturn(false);
|
||||
|
||||
@@ -206,7 +203,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void changePassword_updatesHash_whenCurrentPasswordCorrect() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").password("hashed").build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").password("hashed").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(passwordEncoder.matches("correct", "hashed")).thenReturn(true);
|
||||
when(passwordEncoder.encode("newpass")).thenReturn("newHash");
|
||||
@@ -224,7 +221,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void adminUpdateUser_updatesNameFields() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").build();
|
||||
AppUser user = AppUser.builder().id(id).email("admin@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -241,12 +238,12 @@ class UserServiceTest {
|
||||
void adminUpdateUser_preservesGroups_whenGroupIdsIsNull() {
|
||||
UUID id = UUID.randomUUID();
|
||||
UserGroup adminGroup = UserGroup.builder().id(UUID.randomUUID()).name("Administrators").build();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").groups(Set.of(adminGroup)).build();
|
||||
AppUser user = AppUser.builder().id(id).email("admin@example.com").groups(Set.of(adminGroup)).build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
AdminUpdateUserRequest dto = new AdminUpdateUserRequest();
|
||||
dto.setFirstName("Ada"); // groupIds left null → don't change groups
|
||||
dto.setFirstName("Ada");
|
||||
|
||||
AppUser result = userService.adminUpdateUser(id, dto);
|
||||
|
||||
@@ -258,7 +255,7 @@ class UserServiceTest {
|
||||
UUID id = UUID.randomUUID();
|
||||
UserGroup oldGroup = UserGroup.builder().id(UUID.randomUUID()).name("Viewers").build();
|
||||
UserGroup newGroup = UserGroup.builder().id(UUID.randomUUID()).name("Editors").build();
|
||||
AppUser user = AppUser.builder().id(id).username("max").groups(Set.of(oldGroup)).build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").groups(Set.of(oldGroup)).build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(groupRepository.findAllById(List.of(newGroup.getId()))).thenReturn(List.of(newGroup));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
@@ -273,18 +270,15 @@ class UserServiceTest {
|
||||
|
||||
@Test
|
||||
void adminUpdateUser_clearsGroups_whenGroupIdsIsEmptyList() {
|
||||
// Sending groupIds:[] is the explicit "remove from all groups" signal.
|
||||
// The frontend must NEVER send [] accidentally — it must always include
|
||||
// the currently-selected group checkboxes.
|
||||
UUID id = UUID.randomUUID();
|
||||
UserGroup adminGroup = UserGroup.builder().id(UUID.randomUUID()).name("Administrators").build();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").groups(Set.of(adminGroup)).build();
|
||||
AppUser user = AppUser.builder().id(id).email("admin@example.com").groups(Set.of(adminGroup)).build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(groupRepository.findAllById(List.of())).thenReturn(List.of());
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
AdminUpdateUserRequest dto = new AdminUpdateUserRequest();
|
||||
dto.setGroupIds(List.of()); // empty list → intentional "remove all groups"
|
||||
dto.setGroupIds(List.of());
|
||||
|
||||
AppUser result = userService.adminUpdateUser(id, dto);
|
||||
|
||||
@@ -308,15 +302,14 @@ class UserServiceTest {
|
||||
void createUserOrUpdate_loadsGroups_whenGroupIdsNonEmpty() {
|
||||
UserGroup group = UserGroup.builder().id(UUID.randomUUID()).name("Admins").build();
|
||||
CreateUserRequest req = new CreateUserRequest();
|
||||
req.setUsername("newuser");
|
||||
req.setEmail("u@example.com");
|
||||
req.setInitialPassword("pass");
|
||||
req.setGroupIds(List.of(group.getId()));
|
||||
|
||||
when(userRepository.findByUsername("newuser")).thenReturn(Optional.empty());
|
||||
when(userRepository.findByEmail("u@example.com")).thenReturn(Optional.empty());
|
||||
when(groupRepository.findAllById(List.of(group.getId()))).thenReturn(List.of(group));
|
||||
when(passwordEncoder.encode("pass")).thenReturn("encoded");
|
||||
AppUser saved = AppUser.builder().id(UUID.randomUUID()).username("newuser").build();
|
||||
AppUser saved = AppUser.builder().id(UUID.randomUUID()).email("u@example.com").build();
|
||||
when(userRepository.save(any())).thenReturn(saved);
|
||||
|
||||
AppUser result = userService.createUserOrUpdate(req);
|
||||
@@ -325,7 +318,7 @@ class UserServiceTest {
|
||||
verify(groupRepository).findAllById(List.of(group.getId()));
|
||||
}
|
||||
|
||||
// ─── updateProfile — email edge cases ─────────────────────────────────────
|
||||
// ─── updateProfile — blank email ──────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void updateProfile_throwsBadRequest_whenEmailIsBlank() {
|
||||
@@ -344,12 +337,12 @@ class UserServiceTest {
|
||||
@Test
|
||||
void updateProfile_doesNotChangeEmail_whenEmailDtoIsNull() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").email("keep@example.com").build();
|
||||
AppUser user = AppUser.builder().id(id).email("keep@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
UpdateProfileDTO dto = new UpdateProfileDTO();
|
||||
dto.setEmail(null); // null — no change
|
||||
dto.setEmail(null);
|
||||
|
||||
AppUser result = userService.updateProfile(id, dto);
|
||||
|
||||
@@ -359,7 +352,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void updateProfile_setsContactToNull_whenContactIsBlank() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -376,7 +369,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void adminUpdateUser_setsPassword_whenNewPasswordProvided() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").password("old").build();
|
||||
AppUser user = AppUser.builder().id(id).email("admin@example.com").password("old").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(passwordEncoder.encode("newSecret")).thenReturn("newHashed");
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
@@ -392,7 +385,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void adminUpdateUser_doesNotChangePassword_whenNewPasswordIsBlank() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").password("original").build();
|
||||
AppUser user = AppUser.builder().id(id).email("admin@example.com").password("original").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -423,8 +416,8 @@ class UserServiceTest {
|
||||
void adminUpdateUser_throwsConflict_whenEmailTakenByAnotherUser() {
|
||||
UUID id = UUID.randomUUID();
|
||||
UUID otherId = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").build();
|
||||
AppUser other = AppUser.builder().id(otherId).username("anna").email("taken@example.com").build();
|
||||
AppUser user = AppUser.builder().id(id).email("admin@example.com").build();
|
||||
AppUser other = AppUser.builder().id(otherId).email("taken@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.findByEmail("taken@example.com")).thenReturn(Optional.of(other));
|
||||
|
||||
@@ -494,14 +487,13 @@ class UserServiceTest {
|
||||
@Test
|
||||
void createUserOrUpdate_doesNotLoadGroups_whenGroupIdsIsEmpty() {
|
||||
CreateUserRequest req = new CreateUserRequest();
|
||||
req.setUsername("newuser");
|
||||
req.setEmail("u@example.com");
|
||||
req.setInitialPassword("pass");
|
||||
req.setGroupIds(List.of()); // empty, not null
|
||||
req.setGroupIds(List.of());
|
||||
|
||||
when(userRepository.findByUsername("newuser")).thenReturn(Optional.empty());
|
||||
when(userRepository.findByEmail("u@example.com")).thenReturn(Optional.empty());
|
||||
when(passwordEncoder.encode("pass")).thenReturn("encoded");
|
||||
AppUser saved = AppUser.builder().id(UUID.randomUUID()).username("newuser").build();
|
||||
AppUser saved = AppUser.builder().id(UUID.randomUUID()).email("u@example.com").build();
|
||||
when(userRepository.save(any())).thenReturn(saved);
|
||||
|
||||
userService.createUserOrUpdate(req);
|
||||
@@ -509,12 +501,12 @@ class UserServiceTest {
|
||||
verify(groupRepository, never()).findAllById(any());
|
||||
}
|
||||
|
||||
// ─── updateProfile — contact null ─────────────────────────────────────────
|
||||
// ─── updateProfile — contact ──────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void updateProfile_setsTrimmedContact_whenContactIsNonBlank() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -529,7 +521,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void updateProfile_setsNullContact_whenContactIsNull() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").contact("old contact").build();
|
||||
AppUser user = AppUser.builder().id(id).email("max@example.com").contact("old contact").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -544,15 +536,14 @@ class UserServiceTest {
|
||||
@Test
|
||||
void updateProfile_allowsSameEmail_whenEmailBelongsToSameUser() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("max").email("me@example.com").build();
|
||||
AppUser user = AppUser.builder().id(id).email("me@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.findByEmail("me@example.com")).thenReturn(Optional.of(user)); // same user
|
||||
when(userRepository.findByEmail("me@example.com")).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
UpdateProfileDTO dto = new UpdateProfileDTO();
|
||||
dto.setEmail("me@example.com");
|
||||
|
||||
// Must not throw
|
||||
AppUser result = userService.updateProfile(id, dto);
|
||||
assertThat(result.getEmail()).isEqualTo("me@example.com");
|
||||
}
|
||||
@@ -562,7 +553,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void adminUpdateUser_setsNullContact_whenContactIsNull() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").contact("old contact").build();
|
||||
AppUser user = AppUser.builder().id(id).email("admin@example.com").contact("old contact").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -577,7 +568,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void adminUpdateUser_setsNullContact_whenContactIsBlank() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").contact("old").build();
|
||||
AppUser user = AppUser.builder().id(id).email("admin@example.com").contact("old").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -592,7 +583,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void adminUpdateUser_setsTrimmedContact_whenContactIsNonBlank() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").build();
|
||||
AppUser user = AppUser.builder().id(id).email("admin@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -607,7 +598,7 @@ class UserServiceTest {
|
||||
@Test
|
||||
void adminUpdateUser_doesNotModifyEmail_whenEmailIsNull() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").email("keep@example.com").build();
|
||||
AppUser user = AppUser.builder().id(id).email("keep@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
@@ -622,15 +613,14 @@ class UserServiceTest {
|
||||
@Test
|
||||
void adminUpdateUser_allowsSameEmail_whenEmailBelongsToSameUser() {
|
||||
UUID id = UUID.randomUUID();
|
||||
AppUser user = AppUser.builder().id(id).username("admin").email("me@example.com").build();
|
||||
AppUser user = AppUser.builder().id(id).email("me@example.com").build();
|
||||
when(userRepository.findById(id)).thenReturn(Optional.of(user));
|
||||
when(userRepository.findByEmail("me@example.com")).thenReturn(Optional.of(user)); // same user
|
||||
when(userRepository.findByEmail("me@example.com")).thenReturn(Optional.of(user));
|
||||
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
AdminUpdateUserRequest dto = new AdminUpdateUserRequest();
|
||||
dto.setEmail("me@example.com");
|
||||
|
||||
// Must not throw
|
||||
AppUser result = userService.adminUpdateUser(id, dto);
|
||||
assertThat(result.getEmail()).isEqualTo("me@example.com");
|
||||
}
|
||||
@@ -639,16 +629,14 @@ class UserServiceTest {
|
||||
|
||||
@Test
|
||||
void createUserOrUpdate_doesNotLoadGroups_whenGroupIdsIsNull() {
|
||||
// request.getGroupIds() == null → short-circuit (A=false), groupRepository never called
|
||||
CreateUserRequest req = new CreateUserRequest();
|
||||
req.setUsername("nullgroups");
|
||||
req.setEmail("ng@example.com");
|
||||
req.setInitialPassword("pass");
|
||||
req.setGroupIds(null); // null → first condition false → short-circuit
|
||||
req.setGroupIds(null);
|
||||
|
||||
when(userRepository.findByUsername("nullgroups")).thenReturn(Optional.empty());
|
||||
when(userRepository.findByEmail("ng@example.com")).thenReturn(Optional.empty());
|
||||
when(passwordEncoder.encode("pass")).thenReturn("encoded");
|
||||
AppUser saved = AppUser.builder().id(UUID.randomUUID()).username("nullgroups").build();
|
||||
AppUser saved = AppUser.builder().id(UUID.randomUUID()).email("ng@example.com").build();
|
||||
when(userRepository.save(any())).thenReturn(saved);
|
||||
|
||||
userService.createUserOrUpdate(req);
|
||||
|
||||
Reference in New Issue
Block a user