diff --git a/backend/src/main/java/org/raddatz/familienarchiv/user/AppUser.java b/backend/src/main/java/org/raddatz/familienarchiv/user/AppUser.java index bfc0d525..79528bc1 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/user/AppUser.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/user/AppUser.java @@ -88,7 +88,8 @@ public class AppUser { }; public static String computeColor(UUID id) { - return PALETTE[Math.abs(id.hashCode()) % PALETTE.length]; + // Math.floorMod avoids the Integer.MIN_VALUE overflow trap in Math.abs(hashCode()) + return PALETTE[Math.floorMod(id.hashCode(), PALETTE.length)]; } @PrePersist diff --git a/backend/src/test/java/org/raddatz/familienarchiv/user/AppUserTest.java b/backend/src/test/java/org/raddatz/familienarchiv/user/AppUserTest.java index dfde28cf..6dcf41fe 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/user/AppUserTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/user/AppUserTest.java @@ -35,4 +35,15 @@ class AppUserTest { .count(); assertThat(distinct).isGreaterThan(1); } + + @Test + void computeColor_returnsValidPaletteColorForIntegerMinValueHash() { + // UUID "80000000-0000-0000-0000-000000000000" has hashCode() == Integer.MIN_VALUE. + // Math.abs(Integer.MIN_VALUE) overflows back to Integer.MIN_VALUE (negative), making + // Math.abs(hashCode()) % n unsafe for palette sizes that don't evenly divide MIN_VALUE. + // Math.floorMod eliminates this edge case entirely. + UUID minHashId = UUID.fromString("80000000-0000-0000-0000-000000000000"); + assertThat(minHashId.hashCode()).isEqualTo(Integer.MIN_VALUE); + assertThat(EXPECTED_PALETTE).contains(AppUser.computeColor(minHashId)); + } }