Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package at.drm.util;

import at.drm.dao.RelationDao;
import at.drm.factory.RelationDaoFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class DynamicRelationsUtils {

private final RelationDaoFactory relationDaoFactory;

public List<Class<?>> listRegisteredEntities() {
Set<RelationDao> allDaos = relationDaoFactory.getAllDaos();

return allDaos.stream()
.map(this::extractEntityClassFromDao)
.distinct()
.collect(Collectors.toList());
}

private Class<?> extractEntityClassFromDao(RelationDao relationDao) {
try {
ResolvableType resolvableType = ResolvableType.forClass(relationDao.getClass()).as(RelationDao.class);
ResolvableType generic = resolvableType.getGeneric(0);
Class<?> relationLinkClass = getRelationLinkClass(generic);

Field sourceObjectField = relationLinkClass.getDeclaredField("sourceObject");
return sourceObjectField.getType();
} catch (NoSuchFieldException e) {
throw new RuntimeException(
"Could not find sourceObject field in RelationLink class for DAO: " + relationDao.getClass(), e
);
}
}

private Class<?> getRelationLinkClass(ResolvableType generic) {
Class<?> relationLinkClass = generic.resolve();

if (relationLinkClass == null) {
throw new RuntimeException("Could not resolve RelationLink class for type: " + generic.getClass());
}

return relationLinkClass;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package at.drm.testutil.daos;

import at.drm.dao.RelationDao;
import at.drm.testutil.relations.AnotherTestEntityRelationLink;

public abstract class AnotherTestEntityRelationDao implements RelationDao<AnotherTestEntityRelationLink, Long> { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package at.drm.testutil.daos;

import at.drm.dao.RelationDao;
import at.drm.testutil.relations.InvalidRelationLink;

public abstract class InvalidRelationDao implements RelationDao<InvalidRelationLink, Long> { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package at.drm.testutil.daos;

import at.drm.dao.RelationDao;
import at.drm.testutil.relations.TestEntityRelationLink;

public abstract class TestEntityRelationDao implements RelationDao<TestEntityRelationLink, Long> { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package at.drm.testutil.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@Data
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(onlyExplicitlyIncluded = true)
public class AnotherTestEntity {

@EqualsAndHashCode.Include
@ToString.Include
private Long id;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package at.drm.testutil.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@Data
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(onlyExplicitlyIncluded = true)
public class SomeTestEntity {

@EqualsAndHashCode.Include
@ToString.Include
private Long id;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package at.drm.testutil.relations;

import at.drm.model.RelationLink;
import at.drm.testutil.entities.AnotherTestEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class AnotherTestEntityRelationLink implements RelationLink<AnotherTestEntity> {
private AnotherTestEntity sourceObject;
private Long targetId;
private String targetType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package at.drm.testutil.relations;

import at.drm.model.RelationLink;
import at.drm.testutil.entities.SomeTestEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class InvalidRelationLink implements RelationLink<SomeTestEntity> {
private SomeTestEntity wrongFieldName;
private Long targetId;
private String targetType;

@Override
public SomeTestEntity getSourceObject() {
return wrongFieldName;
}

@Override
public void setSourceObject(SomeTestEntity sourceObject) {
this.wrongFieldName = sourceObject;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package at.drm.testutil.relations;

import at.drm.model.RelationLink;
import at.drm.testutil.entities.SomeTestEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TestEntityRelationLink implements RelationLink<SomeTestEntity> {
private SomeTestEntity sourceObject;
private Long targetId;
private String targetType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package at.drm.util;

import at.drm.factory.RelationDaoFactory;
import at.drm.testutil.daos.AnotherTestEntityRelationDao;
import at.drm.testutil.daos.InvalidRelationDao;
import at.drm.testutil.daos.TestEntityRelationDao;
import at.drm.testutil.entities.AnotherTestEntity;
import at.drm.testutil.entities.SomeTestEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class DynamicRelationsUtilsTest {

@Mock
private RelationDaoFactory relationDaoFactory;

private DynamicRelationsUtils dynamicRelationsUtils;

@BeforeEach
void setUp() {
dynamicRelationsUtils = new DynamicRelationsUtils(relationDaoFactory);
}

@Test
void testListRegisteredEntities_WithMultipleDaos_ShouldReturnDistinctEntityClasses() {
TestEntityRelationDao dao1 = mock(TestEntityRelationDao.class);
AnotherTestEntityRelationDao dao2 = mock(AnotherTestEntityRelationDao.class);

when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao1, dao2));

List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();

assertNotNull(result);
assertEquals(2, result.size());
assertTrue(result.contains(SomeTestEntity.class));
assertTrue(result.contains(AnotherTestEntity.class));
verify(relationDaoFactory, times(1)).getAllDaos();
}

@Test
void testListRegisteredEntities_WithDuplicateEntityTypes_ShouldReturnDistinctClasses() {
TestEntityRelationDao dao1 = mock(TestEntityRelationDao.class);
TestEntityRelationDao dao2 = mock(TestEntityRelationDao.class);

when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao1, dao2));

List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();

assertNotNull(result);
assertEquals(1, result.size());
assertEquals(SomeTestEntity.class, result.getFirst());
verify(relationDaoFactory, times(1)).getAllDaos();
}

@Test
void testListRegisteredEntities_WithEmptyDaoSet_ShouldReturnEmptyList() {
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of());

List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();

assertNotNull(result);
assertTrue(result.isEmpty());
verify(relationDaoFactory, times(1)).getAllDaos();
}

@Test
void testExtractEntityClassFromDao_WithValidDao_ShouldReturnCorrectEntityClass() {
TestEntityRelationDao dao = mock(TestEntityRelationDao.class);
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao));

List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();

assertNotNull(result);
assertEquals(1, result.size());
assertEquals(SomeTestEntity.class, result.getFirst());
}

@Test
void testExtractEntityClassFromDao_WithInvalidRelationLink_ShouldThrowRuntimeException() {
InvalidRelationDao invalidDao = mock(InvalidRelationDao.class);
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(invalidDao));

RuntimeException exception = assertThrows(RuntimeException.class, () -> dynamicRelationsUtils.listRegisteredEntities());

assertTrue(exception.getMessage().contains("Could not find sourceObject field"));
assertTrue(exception.getMessage().contains("InvalidRelationDao"));
verify(relationDaoFactory, times(1)).getAllDaos();
}

@Test
void testListRegisteredEntities_WithSingleDao_ShouldReturnSingleEntity() {
TestEntityRelationDao dao = mock(TestEntityRelationDao.class);
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao));

List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();

assertNotNull(result);
assertEquals(1, result.size());
assertEquals(SomeTestEntity.class, result.getFirst());
verify(relationDaoFactory, times(1)).getAllDaos();
}

@Test
void testListRegisteredEntities_VerifyStreamProcessing() {
TestEntityRelationDao dao1 = mock(TestEntityRelationDao.class);
AnotherTestEntityRelationDao dao2 = mock(AnotherTestEntityRelationDao.class);
TestEntityRelationDao dao3 = mock(TestEntityRelationDao.class);

when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao1, dao2, dao3));

List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();

assertNotNull(result);
assertEquals(2, result.size());
assertTrue(result.contains(SomeTestEntity.class));
assertTrue(result.contains(AnotherTestEntity.class));

assertInstanceOf(List.class, result);
verify(relationDaoFactory, times(1)).getAllDaos();
}
}
21 changes: 21 additions & 0 deletions testing/src/test/java/at/test/drm/ApplicationIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package at.test.drm;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import at.drm.dao.RelationDao;
import at.drm.factory.RelationDaoFactory;
import at.drm.util.DynamicRelationsUtils;
import java.util.List;
import java.util.Set;

Expand Down Expand Up @@ -32,6 +38,10 @@ class ApplicationIntegrationTest {
private RelationService relationService;
@Autowired
private DynamicRelationsPrintService dynamicRelationsPrintService;
@Autowired
private DynamicRelationsUtils dynamicRelationsUtils;
@Autowired
private RelationDaoFactory relationDaoFactory;

@Test
void shouldFindRelationBySourceObject() {
Expand Down Expand Up @@ -165,4 +175,15 @@ void shouldPrintRelationsWithCyclicRelations() {
DocumentEntityType
""");
}

@Test
void testListRegisteredEntities_shouldReturnRegisteredEntities() {
List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();
assertThat(result).hasSize(3);
assertThat(result).containsExactlyInAnyOrder(
PersonEntity.class,
DogEntity.class,
DocumentEntity.class
);
}
}