fix add-on expiry, add tests

This commit is contained in:
2025-11-09 17:22:55 +01:00
parent 13bf19394c
commit 8d9b10bace
4 changed files with 318 additions and 10 deletions
+7
View File
@@ -104,5 +104,12 @@
<artifactId>commons-csv</artifactId> <artifactId>commons-csv</artifactId>
<version>${commons-csv.version}</version> <version>${commons-csv.version}</version>
</dependency> </dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>
@@ -416,11 +416,7 @@ public class PermissionManager implements UserDataProvider {
List<Permission> existingPermissions = get(target, name); List<Permission> existingPermissions = get(target, name);
for (Permission existingPermission : existingPermissions) { for (Permission existingPermission : existingPermissions) {
if (additional && (starts == null && (existingPermission.getStarts() == null if (additional) {
|| existingPermission.getStarts().isBefore(Instant.now()))
|| starts != null && (starts.isBefore(existingPermission.getExpires())
|| existingPermission.getStarts() != null
&& starts.isAfter(existingPermission.getStarts())))) {
if (permission == null) { if (permission == null) {
permission = existingPermission; permission = existingPermission;
} else if (existingPermission.getExpires().isAfter(permission.getExpires())) { } else if (existingPermission.getExpires().isAfter(permission.getExpires())) {
@@ -437,13 +433,23 @@ public class PermissionManager implements UserDataProvider {
permission.setStarts(permissionStarts); permission.setStarts(permissionStarts);
permission.setExpires(permissionsExpires); permission.setExpires(permissionsExpires);
} else { } else {
// For expired permissions, calculate the appropriate future expiry date
if (permission.getExpires() == null || permission.getExpires().isBefore(Instant.now())) { if (permission.getExpires() == null || permission.getExpires().isBefore(Instant.now())) {
permission.setExpires(Instant.now()); Instant baseExpiry = permission.getExpires() != null ? permission.getExpires() : Instant.now();
} Instant newExpiry = baseExpiry;
// Keep adding lifetime periods until we get a date in the future
while (newExpiry.isBefore(Instant.now()) || newExpiry.equals(Instant.now())) {
newExpiry = InstantHelper.plus(newExpiry, permissionMapping.getLifetime(),
permissionMapping.getLifetimeUnit());
}
permission.setExpires(newExpiry);
} else {
// For valid permissions, extend from current expiry
permission.setExpires(InstantHelper.plus(permission.getExpires(), permissionMapping.getLifetime(), permission.setExpires(InstantHelper.plus(permission.getExpires(), permissionMapping.getLifetime(),
permissionMapping.getLifetimeUnit())); permissionMapping.getLifetimeUnit()));
} }
}
if (permission.getStarts() != null && permission.getStarts().isBefore(Instant.now())) { if (permission.getStarts() != null && permission.getStarts().isBefore(Instant.now())) {
permission.setStarts(null); permission.setStarts(null);
@@ -0,0 +1,295 @@
package de.bstly.we.businesslogic;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Sort;
import com.google.gson.JsonArray;
import de.bstly.we.model.Permission;
import de.bstly.we.model.PermissionMapping;
import de.bstly.we.model.QPermission;
import de.bstly.we.repository.PermissionRepository;
@ExtendWith(MockitoExtension.class)
class PermissionManagerTest {
@Mock
private PermissionRepository permissionRepository;
@Mock
private PermissionMappingManager permissionMappingManager;
@InjectMocks
private PermissionManager permissionManager;
private static final Long TARGET_USER_ID = 1L;
private static final Integer TEST_ITEM_ID = 1;
private static final String ROLE_MEMBER = "ROLE_MEMBER";
@Test
void testGetForItem_NewAddonPermission_CreatesNewWhenNoExisting() {
// Given
Instant currentTime = Instant.parse("2025-03-15T10:00:00Z");
PermissionMapping addonMapping = createAddonPermissionMapping(ROLE_MEMBER, 1L, ChronoUnit.YEARS, true);
when(permissionMappingManager.getAllByItem(TEST_ITEM_ID)).thenReturn(Arrays.asList(addonMapping));
// No existing permissions
when(permissionRepository.findAll(any(com.querydsl.core.types.Predicate.class), any(Sort.class)))
.thenReturn(Arrays.asList());
// When
List<Permission> result = permissionManager.getForItem(TARGET_USER_ID, TEST_ITEM_ID, new JsonArray(),
currentTime, null);
// Then
assertEquals(1, result.size());
Permission permission = result.get(0);
assertEquals(TARGET_USER_ID, permission.getTarget());
assertEquals(ROLE_MEMBER, permission.getName());
assertTrue(permission.isAddon()); // Should be addon since no existing permission found
// Should create new permission with 1 year from current time: March 15, 2025 + 1 year = March 15, 2026
Instant expectedExpiry = Instant.parse("2026-03-15T10:00:00Z");
assertEquals(expectedExpiry, permission.getExpires());
}
@Test
void testGetForItem_AddonExtendsValidPermission_Simple() {
// Given - existing valid permission that expires on June 15, 2025
Instant currentTime = Instant.parse("2025-03-15T10:00:00Z");
Instant existingExpiry = Instant.parse("2025-06-15T10:00:00Z"); // 3 months in the future
Permission existingPermission = createPermission(TARGET_USER_ID, ROLE_MEMBER, false, null, existingExpiry);
PermissionMapping addonMapping = createAddonPermissionMapping(ROLE_MEMBER, 1L, ChronoUnit.YEARS, true);
when(permissionMappingManager.getAllByItem(TEST_ITEM_ID)).thenReturn(Arrays.asList(addonMapping));
when(permissionRepository.findAll(any(com.querydsl.core.types.Predicate.class), any(Sort.class)))
.thenReturn(Arrays.asList(existingPermission));
// When
List<Permission> result = permissionManager.getForItem(TARGET_USER_ID, TEST_ITEM_ID, new JsonArray(),
currentTime, null);
// Then
assertEquals(1, result.size());
Permission extendedPermission = result.get(0);
assertEquals(existingPermission, extendedPermission); // Should be the same object (extended)
// Should extend by exactly 1 year from current expiry: June 15, 2025 + 1 year = June 15, 2026
Instant expectedExpiry = Instant.parse("2026-06-15T10:00:00Z");
assertEquals(expectedExpiry, extendedPermission.getExpires());
assertEquals(TARGET_USER_ID, extendedPermission.getTarget());
assertEquals(ROLE_MEMBER, extendedPermission.getName());
}
@Test
void testGetForItem_AddonExtendsExpiredPermission_SimpleCase() {
// Given - existing permission that expired on February 15, 2025 (1 month ago from March 15)
Instant currentTime = Instant.parse("2025-03-15T10:00:00Z");
Instant expiredDate = Instant.parse("2025-02-15T10:00:00Z"); // Expired exactly 1 month ago
Permission expiredPermission = createPermission(TARGET_USER_ID, ROLE_MEMBER, false, null, expiredDate);
PermissionMapping addonMapping = createAddonPermissionMapping(ROLE_MEMBER, 1L, ChronoUnit.YEARS, true);
when(permissionMappingManager.getAllByItem(TEST_ITEM_ID)).thenReturn(Arrays.asList(addonMapping));
when(permissionRepository.findAll(any(com.querydsl.core.types.Predicate.class), any(Sort.class)))
.thenReturn(Arrays.asList(expiredPermission));
// When
List<Permission> result = permissionManager.getForItem(TARGET_USER_ID, TEST_ITEM_ID, new JsonArray(),
currentTime, null);
// Then
assertEquals(1, result.size());
Permission extendedPermission = result.get(0);
assertEquals(expiredPermission, extendedPermission); // Should be the same object (extended)
// Should extend by exactly 1 year from original expiry: Feb 15, 2025 + 1 year = Feb 15, 2026
Instant expectedExpiry = Instant.parse("2026-02-15T10:00:00Z");
assertEquals(expectedExpiry, extendedPermission.getExpires());
assertTrue(extendedPermission.getExpires().isAfter(currentTime)); // Should be in the future
}
@Test
void testGetForItem_AddonExtendsLongExpiredPermission_CatchesUp() {
// Given - permission that expired 2 years ago (Dec 31, 2023, applied in Nov 2025)
Instant currentTime = Instant.parse("2025-02-09T10:00:00Z");
Instant longExpiredDate = Instant.parse("2023-12-31T23:59:59Z");
Permission longExpiredPermission = createPermission(TARGET_USER_ID, ROLE_MEMBER, false, null, longExpiredDate);
PermissionMapping addonMapping = createAddonPermissionMapping(ROLE_MEMBER, 1L, ChronoUnit.YEARS, true);
when(permissionMappingManager.getAllByItem(TEST_ITEM_ID)).thenReturn(Arrays.asList(addonMapping));
when(permissionRepository.findAll(any(com.querydsl.core.types.Predicate.class), any(Sort.class)))
.thenReturn(Arrays.asList(longExpiredPermission));
// When
List<Permission> result = permissionManager.getForItem(TARGET_USER_ID, TEST_ITEM_ID, new JsonArray(),
currentTime, null);
// Then
assertEquals(1, result.size());
Permission extendedPermission = result.get(0);
assertEquals(longExpiredPermission, extendedPermission); // Should be the same object (extended)
// Should "catch up" to future: 2023-12-31 -> 2024-12-31 -> 2025-12-31 (first future date)
Instant expectedExpiry = Instant.parse("2025-12-31T23:59:59Z");
assertEquals(expectedExpiry, extendedPermission.getExpires());
assertTrue(extendedPermission.getExpires().isAfter(currentTime)); // Should be in the future
}
@Test
void testGetForItem_AddonExtendsNonExpiredPermission_CatchesUp() {
// Given - permission that expired 2 years ago (Dec 31, 2023, applied in Nov 2025)
Instant currentTime = Instant.parse("2025-02-09T10:00:00Z");
Instant expiredDate = Instant.parse("2025-12-31T23:59:59Z");
Permission existingPermission = createPermission(TARGET_USER_ID, ROLE_MEMBER, false, null, expiredDate);
PermissionMapping addonMapping = createAddonPermissionMapping(ROLE_MEMBER, 1L, ChronoUnit.YEARS, true);
when(permissionMappingManager.getAllByItem(TEST_ITEM_ID)).thenReturn(Arrays.asList(addonMapping));
when(permissionRepository.findAll(any(com.querydsl.core.types.Predicate.class), any(Sort.class)))
.thenReturn(Arrays.asList(existingPermission));
// When
List<Permission> result = permissionManager.getForItem(TARGET_USER_ID, TEST_ITEM_ID, new JsonArray(),
currentTime, null);
// Then
assertEquals(1, result.size());
Permission extendedPermission = result.get(0);
assertEquals(existingPermission, extendedPermission); // Should be the same object (extended)
// Should "catch up" to future: 2025-12-31 -> 2026-12-31 (first future date)
Instant expectedExpiry = Instant.parse("2026-12-31T23:59:59Z");
assertEquals(expectedExpiry, extendedPermission.getExpires());
assertTrue(extendedPermission.getExpires().isAfter(currentTime)); // Should be in the future
}
@Test
void testGetForItem_AddonSelectsLatestExpiringPermission() {
// Given - multiple existing permissions with different expiry dates
Instant currentTime = Instant.parse("2025-03-15T10:00:00Z");
Permission permission1 = createPermission(TARGET_USER_ID, ROLE_MEMBER, false, null, Instant.parse("2025-06-23T10:00:00Z")); // 100 days
Permission permission2 = createPermission(TARGET_USER_ID, ROLE_MEMBER, false, null, Instant.parse("2025-10-01T10:00:00Z")); // 200 days (latest)
Permission permission3 = createPermission(TARGET_USER_ID, ROLE_MEMBER, false, null, Instant.parse("2025-05-04T10:00:00Z")); // 50 days
PermissionMapping addonMapping = createAddonPermissionMapping(ROLE_MEMBER, 6L, ChronoUnit.MONTHS, true);
when(permissionMappingManager.getAllByItem(TEST_ITEM_ID)).thenReturn(Arrays.asList(addonMapping));
when(permissionRepository.findAll(any(com.querydsl.core.types.Predicate.class), any(Sort.class)))
.thenReturn(Arrays.asList(permission1, permission2, permission3));
// When
List<Permission> result = permissionManager.getForItem(TARGET_USER_ID, TEST_ITEM_ID, new JsonArray(),
currentTime, null);
// Then
assertEquals(1, result.size());
Permission selectedPermission = result.get(0);
assertEquals(permission2, selectedPermission); // Should select the one with latest expiry (Oct 1, 2025)
// Should extend by 6 months from latest expiry: Oct 1, 2025 + 6 months = April 1, 2026
Instant expectedExpiry = Instant.parse("2026-04-01T10:00:00Z");
assertEquals(expectedExpiry, selectedPermission.getExpires());
assertEquals(TARGET_USER_ID, selectedPermission.getTarget());
assertEquals(ROLE_MEMBER, selectedPermission.getName());
}
@Test
void testGetForItem_NonAddonCreatesNewPermission() {
// Given
Instant currentTime = Instant.parse("2025-03-15T10:00:00Z");
PermissionMapping nonAddonMapping = createAddonPermissionMapping(ROLE_MEMBER, 1L, ChronoUnit.YEARS, false); // Not addon
when(permissionMappingManager.getAllByItem(TEST_ITEM_ID)).thenReturn(Arrays.asList(nonAddonMapping));
// Existing permission exists but should be ignored since this is not an addon
Permission existingPermission = createPermission(TARGET_USER_ID, ROLE_MEMBER, false, null, Instant.parse("2025-06-23T10:00:00Z"));
when(permissionRepository.findAll(any(com.querydsl.core.types.Predicate.class), any(Sort.class)))
.thenReturn(Arrays.asList(existingPermission));
// When
List<Permission> result = permissionManager.getForItem(TARGET_USER_ID, TEST_ITEM_ID, new JsonArray(),
currentTime, null);
// Then
assertEquals(1, result.size());
Permission newPermission = result.get(0);
assertNotEquals(existingPermission, newPermission); // Should be a new permission object
assertEquals(TARGET_USER_ID, newPermission.getTarget());
assertEquals(ROLE_MEMBER, newPermission.getName());
assertFalse(newPermission.isAddon()); // Should not be addon
// Should create new permission with 1 year from current time: March 15, 2025 + 1 year = March 15, 2026
Instant expectedExpiry = Instant.parse("2026-03-15T10:00:00Z");
assertEquals(expectedExpiry, newPermission.getExpires());
}
@Test
void testGetForItem_MultiplePermissionNames() {
// Given - mapping with multiple permission names
Instant currentTime = Instant.parse("2025-03-15T10:00:00Z");
Set<String> permissionNames = new HashSet<>(Arrays.asList("ROLE_MEMBER", "ROLE_SPECIAL"));
PermissionMapping multiMapping = createPermissionMapping(permissionNames, 1L, ChronoUnit.YEARS, true);
when(permissionMappingManager.getAllByItem(TEST_ITEM_ID)).thenReturn(Arrays.asList(multiMapping));
when(permissionRepository.findAll(any(com.querydsl.core.types.Predicate.class), any(Sort.class)))
.thenReturn(Arrays.asList()); // No existing permissions
// When
List<Permission> result = permissionManager.getForItem(TARGET_USER_ID, TEST_ITEM_ID, new JsonArray(),
currentTime, null);
// Then
assertEquals(2, result.size());
// Should have both permission names
Set<String> resultNames = new HashSet<>();
for (Permission p : result) {
resultNames.add(p.getName());
assertEquals(TARGET_USER_ID, p.getTarget());
assertTrue(p.isAddon());
}
assertEquals(permissionNames, resultNames);
}
// Helper methods
private Permission createPermission(Long target, String name, boolean addon, Instant starts, Instant expires) {
Permission permission = new Permission();
permission.setTarget(target);
permission.setName(name);
permission.setAddon(addon);
permission.setStarts(starts);
permission.setExpires(expires);
return permission;
}
private PermissionMapping createAddonPermissionMapping(String permissionName, Long lifetime, ChronoUnit unit, boolean isAddon) {
Set<String> names = new HashSet<>();
names.add(permissionName);
return createPermissionMapping(names, lifetime, unit, isAddon);
}
private PermissionMapping createPermissionMapping(Set<String> names, Long lifetime, ChronoUnit unit, boolean isAddon) {
PermissionMapping mapping = new PermissionMapping();
mapping.setItem(TEST_ITEM_ID);
mapping.setNames(names);
mapping.setLifetime(lifetime);
mapping.setLifetimeUnit(unit);
mapping.setAddon(isAddon);
mapping.setLifetimeRound(false);
return mapping;
}
}
+1 -1
View File
@@ -22,7 +22,7 @@
<unit-api.version>2.2</unit-api.version> <unit-api.version>2.2</unit-api.version>
<commons-csv.version>1.14.1</commons-csv.version> <commons-csv.version>1.14.1</commons-csv.version>
<dnsjava.version>3.6.3</dnsjava.version> <dnsjava.version>3.6.3</dnsjava.version>
<revision>3.5.0</revision> <revision>3.6.0</revision>
</properties> </properties>
<parent> <parent>