generalized AccessMappingsUnitTestBase

This commit is contained in:
Michael Hoennig 2019-04-28 12:02:01 +02:00
parent 0257c83fa5
commit 2cd92d4e2c
2 changed files with 117 additions and 50 deletions

View File

@ -1,14 +1,19 @@
package org.hostsharing.hsadminng.service.dto; package org.hostsharing.hsadminng.service.dto;
import org.apache.commons.lang3.RandomUtils;
import org.hostsharing.hsadminng.service.accessfilter.AccessFor; import org.hostsharing.hsadminng.service.accessfilter.AccessFor;
import org.hostsharing.hsadminng.service.accessfilter.Role; import org.hostsharing.hsadminng.service.accessfilter.Role;
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
import org.junit.Test;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.removeEnd;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
@ -16,28 +21,71 @@ import static org.assertj.core.api.Assertions.assertThat;
* DTOs which implement AccessMapping are more like a DSL, * DTOs which implement AccessMapping are more like a DSL,
* this base class should be used to enforce its required structure. * this base class should be used to enforce its required structure.
*/ */
public abstract class AccessMappingsUnitTestBase { public abstract class AccessMappingsUnitTestBase<D> {
protected AccessRightsMatcher initAccesFor(final Class<AssetDTO> dtoClass, final Role role) { @Test
public void shouldConvertToString() {
final D sampleDto = createSampleDto(1234L);
final String dtoAsString = dtoToString(sampleDto);
assertThat(sampleDto.toString()).isEqualTo(dtoAsString);
}
@Test
@SuppressWarnings("all")
public void shouldImplementEqualsJustUsingClassAndId() {
final D dto = createSampleDto(1234L);
assertThat(dto.equals(dto)).isTrue();
final D dtoWithSameId = createRandomDto(1234L);
assertThat(dto.equals(dtoWithSameId)).isTrue();
final D dtoWithAnotherId = createRandomDto(RandomUtils.nextLong(2000, 9999));
assertThat(dtoWithAnotherId.equals(dtoWithSameId)).isFalse();
final D dtoWithoutId = createRandomDto(null);
assertThat(dto.equals(dtoWithoutId)).isFalse();
assertThat(dtoWithoutId.equals(dto)).isFalse();
assertThat(dto.equals(null)).isFalse();
assertThat(dto.equals("")).isFalse();
}
@Test
public void shouldImplementHashCodeJustUsingClassAndId() {
final long randomId = RandomUtils.nextLong();
final D dto = createSampleDto(randomId);
assertThat(dto.hashCode()).isEqualTo(Objects.hashCode(randomId));
final D dtoWithoutId = createRandomDto(null);
assertThat(dtoWithoutId.hashCode()).isEqualTo(Objects.hashCode(null));
}
protected abstract D createSampleDto(final Long id);
protected abstract D createRandomDto(final Long id);
protected AccessRightsMatcher initAccessFor(final Class<D> dtoClass, final Role role) {
return new AccessRightsMatcher(dtoClass, role, AccessFor::init); return new AccessRightsMatcher(dtoClass, role, AccessFor::init);
} }
protected AccessRightsMatcher updateAccesFor(final Class<AssetDTO> dtoClass, final Role role) { protected AccessRightsMatcher updateAccessFor(final Class<D> dtoClass, final Role role) {
return new AccessRightsMatcher(dtoClass, role, AccessFor::update); return new AccessRightsMatcher(dtoClass, role, AccessFor::update);
} }
protected AccessRightsMatcher readAccesFor(final Class<AssetDTO> dtoClass, final Role role) { protected AccessRightsMatcher readAccessFor(final Class<D> dtoClass, final Role role) {
return new AccessRightsMatcher(dtoClass, role, AccessFor::read); return new AccessRightsMatcher(dtoClass, role, AccessFor::read);
} }
// This class should have the same generics as the outer class, but then the
// method references (AccessFor::*) can't be resolved anymore by the Java compiler.
protected static class AccessRightsMatcher { protected static class AccessRightsMatcher {
private final Class<AssetDTO> dtoClass; private final Object dtoClass;
private final Role role; private final Role role;
private final String[] namesOfFieldsWithAccessForAnnotation; private final String[] namesOfFieldsWithAccessForAnnotation;
private final String[] namesOfAccessibleFields; private final String[] namesOfAccessibleFields;
AccessRightsMatcher(final Class<AssetDTO> dtoClass, final Role role, final Function<AccessFor, Role[]> access) { AccessRightsMatcher(final Class dtoClass, final Role role, final Function<AccessFor, Role[]> access) {
this.dtoClass = dtoClass; this.dtoClass = dtoClass;
this.role = role; this.role = role;
@ -61,7 +109,7 @@ public abstract class AccessMappingsUnitTestBase {
} }
private static Set<Field> determineFieldsWithAccessForAnnotation(final Class<AssetDTO> dtoClass) { private static Set<Field> determineFieldsWithAccessForAnnotation(final Class<?> dtoClass) {
final Set<Field> fieldsWithAccessForAnnotation = new HashSet<>(); final Set<Field> fieldsWithAccessForAnnotation = new HashSet<>();
@ -83,4 +131,47 @@ public abstract class AccessMappingsUnitTestBase {
return false; return false;
} }
} }
private String dtoToString(final D dto) {
final StringBuilder fieldValues = new StringBuilder();
boolean firstField = true;
for (Field field : dto.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(AccessFor.class)) {
firstField = appendCommaOptionally(fieldValues, firstField);
appendFieldName(fieldValues, field);
appendFieldValue(dto, fieldValues, field);
}
}
return dto.getClass().getSimpleName() + "{" + fieldValues + "}";
}
private void appendFieldValue(final D dto, final StringBuilder fieldValues, final Field field) {
final Object value = ReflectionUtil.getValue(dto, field);
final boolean inQuotes = isJHipsterToStringUsingQuotes(field);
if (inQuotes) {
fieldValues.append("'");
}
fieldValues.append(value);
if (inQuotes) {
fieldValues.append("'");
}
}
private void appendFieldName(final StringBuilder fieldValues, final Field field) {
fieldValues.append(removeEnd(field.getName(), "Id"));
fieldValues.append("=");
}
private boolean appendCommaOptionally(final StringBuilder fieldValues, boolean firstField) {
if (firstField) {
firstField = false;
} else {
fieldValues.append(", ");
}
return firstField;
}
private boolean isJHipsterToStringUsingQuotes(final Field field) {
return !Number.class.isAssignableFrom(field.getType()) && !Boolean.class.isAssignableFrom(field.getType());
}
} }

View File

@ -9,82 +9,59 @@ import org.junit.Test;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import static org.assertj.core.api.Assertions.assertThat; public class AssetDTOUnitTest extends AccessMappingsUnitTestBase<AssetDTO> {
public class AssetDTOUnitTest extends AccessMappingsUnitTestBase {
@Test @Test
public void shouldHaveProperAccessForAdmin() { public void shouldHaveProperAccessForAdmin() {
initAccesFor(AssetDTO.class, Role.ADMIN).shouldBeExactlyFor( initAccessFor(AssetDTO.class, Role.ADMIN).shouldBeExactlyFor(
"membershipId", "documentDate", "amount", "action", "valueDate", "remark"); "membershipId", "documentDate", "amount", "action", "valueDate", "remark");
updateAccesFor(AssetDTO.class, Role.ADMIN).shouldBeExactlyFor("remark"); updateAccessFor(AssetDTO.class, Role.ADMIN).shouldBeExactlyFor("remark");
readAccesFor(AssetDTO.class, Role.ADMIN).shouldBeForAllFields(); readAccessFor(AssetDTO.class, Role.ADMIN).shouldBeForAllFields();
} }
@Test @Test
public void shouldHaveProperAccessForContractualContact() { public void shouldHaveProperAccessForContractualContact() {
initAccesFor(AssetDTO.class, Role.CONTRACTUAL_CONTACT).shouldBeForNothing(); initAccessFor(AssetDTO.class, Role.CONTRACTUAL_CONTACT).shouldBeForNothing();
updateAccesFor(AssetDTO.class, Role.CONTRACTUAL_CONTACT).shouldBeForNothing(); updateAccessFor(AssetDTO.class, Role.CONTRACTUAL_CONTACT).shouldBeForNothing();
readAccesFor(AssetDTO.class, Role.CONTRACTUAL_CONTACT).shouldBeExactlyFor( readAccessFor(AssetDTO.class, Role.CONTRACTUAL_CONTACT).shouldBeExactlyFor(
"id", "membershipId", "documentDate", "amount", "action", "valueDate", "membershipDisplayLabel"); "id", "membershipId", "documentDate", "amount", "action", "valueDate", "membershipDisplayLabel");
} }
@Test @Test
public void shouldHaveNoAccessForTechnicalContact() { public void shouldHaveNoAccessForTechnicalContact() {
initAccesFor(AssetDTO.class, Role.TECHNICAL_CONTACT).shouldBeForNothing(); initAccessFor(AssetDTO.class, Role.TECHNICAL_CONTACT).shouldBeForNothing();
updateAccesFor(AssetDTO.class, Role.TECHNICAL_CONTACT).shouldBeForNothing(); updateAccessFor(AssetDTO.class, Role.TECHNICAL_CONTACT).shouldBeForNothing();
readAccesFor(AssetDTO.class, Role.TECHNICAL_CONTACT).shouldBeForNothing(); readAccessFor(AssetDTO.class, Role.TECHNICAL_CONTACT).shouldBeForNothing();
} }
@Test @Test
public void shouldHaveNoAccessForNormalUsersWithinCustomerRealm() { public void shouldHaveNoAccessForNormalUsersWithinCustomerRealm() {
initAccesFor(AssetDTO.class, Role.ANY_CUSTOMER_USER).shouldBeForNothing(); initAccessFor(AssetDTO.class, Role.ANY_CUSTOMER_USER).shouldBeForNothing();
updateAccesFor(AssetDTO.class, Role.ANY_CUSTOMER_USER).shouldBeForNothing(); updateAccessFor(AssetDTO.class, Role.ANY_CUSTOMER_USER).shouldBeForNothing();
readAccesFor(AssetDTO.class, Role.ANY_CUSTOMER_USER).shouldBeForNothing(); readAccessFor(AssetDTO.class, Role.ANY_CUSTOMER_USER).shouldBeForNothing();
}
@Test
public void shouldConvertToString() {
final AssetDTO dto = createDto(1234L);
assertThat(dto.toString()).isEqualTo("AssetDTO{id=1234, documentDate='2000-12-07', valueDate='2000-12-18', action='PAYMENT', amount=512.01, remark='Some Remark', membership=888, membershipDisplayLabel='Some Membership'}");
}
@Test
public void shouldImplementEqualsJustUsingClassAndId() {
final AssetDTO dto = createDto(1234L);
assertThat(dto.equals(dto)).isTrue();
final AssetDTO dtoWithSameId = createRandomDto(1234L);
assertThat(dto.equals(dtoWithSameId)).isTrue();
final AssetDTO dtoWithAnotherId = createRandomDto(RandomUtils.nextLong(2000, 9999));
assertThat(dtoWithAnotherId.equals(dtoWithSameId)).isFalse();
final AssetDTO dtoWithoutId = createRandomDto(null);
assertThat(dto.equals(dtoWithoutId)).isFalse();
assertThat(dtoWithoutId.equals(dto)).isFalse();
assertThat(dto.equals(null)).isFalse();
assertThat(dto.equals("")).isFalse();
} }
// --- only test fixture below --- // --- only test fixture below ---
private AssetDTO createDto(final Long id) { @Override
public AssetDTO createSampleDto(final Long id) {
final AssetDTO dto = new AssetDTO(); final AssetDTO dto = new AssetDTO();
dto.setId(id); dto.setId(id);
dto.setDocumentDate(LocalDate.parse("2000-12-07")); dto.setDocumentDate(LocalDate.parse("2000-12-07"));
dto.setAmount(new BigDecimal("512.01")); dto.setAmount(new BigDecimal("512.01"));
dto.setAction(AssetAction.PAYMENT); dto.setAction(AssetAction.PAYMENT);
dto.setRemark("Some Remark"); dto.setRemark("Some Remark");
dto.setRemark(null);
dto.setValueDate(LocalDate.parse("2000-12-18")); dto.setValueDate(LocalDate.parse("2000-12-18"));
dto.setMembershipId(888L); dto.setMembershipId(888L);
dto.setMembershipId(null);
dto.setMembershipDisplayLabel("Some Membership"); dto.setMembershipDisplayLabel("Some Membership");
return dto; return dto;
} }
private AssetDTO createRandomDto(final Long id) { @Override
public AssetDTO createRandomDto(final Long id) {
final AssetDTO dto = new AssetDTO(); final AssetDTO dto = new AssetDTO();
dto.setId(id); dto.setId(id);
final LocalDate randomDate = LocalDate.parse("2000-12-07").plusDays(RandomUtils.nextInt(1, 999)); final LocalDate randomDate = LocalDate.parse("2000-12-07").plusDays(RandomUtils.nextInt(1, 999));
@ -97,5 +74,4 @@ public class AssetDTOUnitTest extends AccessMappingsUnitTestBase {
dto.setMembershipDisplayLabel(RandomStringUtils.randomAlphabetic(20)); dto.setMembershipDisplayLabel(RandomStringUtils.randomAlphabetic(20));
return dto; return dto;
} }
} }