improved Stringify
This commit is contained in:
parent
31854bb838
commit
d3312c4444
@ -273,7 +273,7 @@ pitest {
|
|||||||
// As Java unit tests are pretty pointless in our case, this maybe makes not much sense.
|
// As Java unit tests are pretty pointless in our case, this maybe makes not much sense.
|
||||||
mutationThreshold = 71
|
mutationThreshold = 71
|
||||||
coverageThreshold = 57
|
coverageThreshold = 57
|
||||||
testStrengthThreshold = 88
|
testStrengthThreshold = 87
|
||||||
|
|
||||||
outputFormats = ['XML', 'HTML']
|
outputFormats = ['XML', 'HTML']
|
||||||
timestampedReports = false
|
timestampedReports = false
|
||||||
|
@ -4,22 +4,26 @@ import javax.validation.constraints.NotNull;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
// TODO.refa: use this instead of toDisplayName everywhere and add JavaDoc
|
import static java.lang.Boolean.TRUE;
|
||||||
public class Stringify<B> {
|
|
||||||
|
public final class Stringify<B> {
|
||||||
|
|
||||||
private final Class<B> clazz;
|
private final Class<B> clazz;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final List<Property<B>> props = new ArrayList<>();
|
private final List<Property<B>> props = new ArrayList<>();
|
||||||
|
private String separator = ", ";
|
||||||
|
private Boolean quotedValues = null;
|
||||||
|
|
||||||
public static <B> Stringify<B> stringify(final Class<B> clazz, final String name) {
|
public static <B> Stringify<B> stringify(final Class<B> clazz, final String name) {
|
||||||
return new Stringify<B>(clazz, name);
|
return new Stringify<>(clazz, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <B> Stringify<B> stringify(final Class<B> clazz) {
|
public static <B> Stringify<B> stringify(final Class<B> clazz) {
|
||||||
return new Stringify<B>(clazz, null);
|
return new Stringify<>(clazz, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stringify(final Class<B> clazz, final String name) {
|
private Stringify(final Class<B> clazz, final String name) {
|
||||||
@ -28,7 +32,12 @@ public class Stringify<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Stringify<B> withProp(final String propName, final Function<B, ?> getter) {
|
public Stringify<B> withProp(final String propName, final Function<B, ?> getter) {
|
||||||
props.add(new Property<B>(propName, getter));
|
props.add(new Property<>(propName, getter));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stringify<B> withProp(final Function<B, ?> getter) {
|
||||||
|
props.add(new Property<>(null, getter));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,20 +51,43 @@ public class Stringify<B> {
|
|||||||
}
|
}
|
||||||
return propVal;
|
return propVal;
|
||||||
})
|
})
|
||||||
.map(propVal -> propVal.prop.name + "=" + optionallyQuoted(propVal))
|
.map(propVal -> propName(propVal, "=") + optionallyQuoted(propVal))
|
||||||
.collect(Collectors.joining(", "));
|
.collect(Collectors.joining(separator));
|
||||||
return (name != null ? name : object.getClass().getSimpleName()) + "(" + propValues + ")";
|
return (name != null ? name : object.getClass().getSimpleName()) + "(" + propValues + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stringify<B> withSeparator(final String separator) {
|
||||||
|
this.separator = separator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String propName(final PropertyValue<B> propVal, final String delimiter) {
|
||||||
|
return Optional.ofNullable(propVal.prop.name).map(v -> v + delimiter).orElse("");
|
||||||
|
}
|
||||||
|
|
||||||
private String optionallyQuoted(final PropertyValue<B> propVal) {
|
private String optionallyQuoted(final PropertyValue<B> propVal) {
|
||||||
return (propVal.rawValue instanceof Number) || (propVal.rawValue instanceof Boolean)
|
if (quotedValues == null)
|
||||||
? propVal.value
|
return quotedQuotedValueType(propVal)
|
||||||
: "'" + propVal.value + "'";
|
? ("'" + propVal.value + "'")
|
||||||
|
: propVal.value;
|
||||||
|
return TRUE == quotedValues
|
||||||
|
? ("'" + propVal.value + "'")
|
||||||
|
: propVal.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <B> boolean quotedQuotedValueType(final PropertyValue<B> propVal) {
|
||||||
|
return !(propVal.rawValue instanceof Number || propVal.rawValue instanceof Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stringify<B> quotedValues(final boolean quotedValues) {
|
||||||
|
this.quotedValues = quotedValues;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private record Property<B>(String name, Function<B, ?> getter) {}
|
private record Property<B>(String name, Function<B, ?> getter) {}
|
||||||
|
|
||||||
private record PropertyValue<B>(Property<B> prop, Object rawValue, String value) {
|
private record PropertyValue<B>(Property<B> prop, Object rawValue, String value) {
|
||||||
|
|
||||||
static <B> PropertyValue<B> of(Property<B> prop, Object rawValue) {
|
static <B> PropertyValue<B> of(Property<B> prop, Object rawValue) {
|
||||||
return rawValue != null ? new PropertyValue<>(prop, rawValue, rawValue.toString()) : null;
|
return rawValue != null ? new PropertyValue<>(prop, rawValue, rawValue.toString()) : null;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.partner;
|
package net.hostsharing.hsadminng.hs.office.partner;
|
||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
import net.hostsharing.hsadminng.Stringify;
|
||||||
|
import net.hostsharing.hsadminng.Stringifyable;
|
||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
|
|
||||||
@ -8,6 +10,8 @@ import javax.persistence.*;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "hs_office_partner_rv")
|
@Table(name = "hs_office_partner_rv")
|
||||||
@Getter
|
@Getter
|
||||||
@ -15,7 +19,13 @@ import java.util.UUID;
|
|||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class HsOfficePartnerEntity {
|
public class HsOfficePartnerEntity implements Stringifyable {
|
||||||
|
|
||||||
|
private static Stringify<HsOfficePartnerEntity> stringify = stringify(HsOfficePartnerEntity.class, "partner")
|
||||||
|
.withProp(HsOfficePartnerEntity::getPerson)
|
||||||
|
.withProp(HsOfficePartnerEntity::getContact)
|
||||||
|
.withSeparator(": ")
|
||||||
|
.quotedValues(false);
|
||||||
|
|
||||||
private @Id UUID uuid;
|
private @Id UUID uuid;
|
||||||
|
|
||||||
@ -33,7 +43,13 @@ public class HsOfficePartnerEntity {
|
|||||||
private @Column(name = "birthday") LocalDate birthday;
|
private @Column(name = "birthday") LocalDate birthday;
|
||||||
private @Column(name = "dateofdeath") LocalDate dateOfDeath;
|
private @Column(name = "dateofdeath") LocalDate dateOfDeath;
|
||||||
|
|
||||||
public String getDisplayName() {
|
@Override
|
||||||
return "partner(%s, %s)".formatted(person.getDisplayName(), contact.getLabel());
|
public String toString() {
|
||||||
|
return stringify.apply(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toShortString() {
|
||||||
|
return person.toShortString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,17 @@ package net.hostsharing.hsadminng;
|
|||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.FieldNameConstants;
|
import lombok.experimental.FieldNameConstants;
|
||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import javax.persistence.Column;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.Stringify.stringify;
|
import static net.hostsharing.hsadminng.Stringify.stringify;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class StringifyUnitTest {
|
class StringifyUnitTest {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Builder
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@FieldNameConstants
|
@FieldNameConstants
|
||||||
@ -27,15 +20,20 @@ class StringifyUnitTest {
|
|||||||
|
|
||||||
private static Stringify<TestBean> toString = stringify(TestBean.class, "bean")
|
private static Stringify<TestBean> toString = stringify(TestBean.class, "bean")
|
||||||
.withProp(TestBean.Fields.label, TestBean::getLabel)
|
.withProp(TestBean.Fields.label, TestBean::getLabel)
|
||||||
.withProp(TestBean.Fields.content, TestBean::getContent)
|
.withProp(TestBean.Fields.contentA, TestBean::getContentA)
|
||||||
|
.withProp(TestBean.Fields.contentB, TestBean::getContentB)
|
||||||
|
.withProp(TestBean.Fields.value, TestBean::getValue)
|
||||||
.withProp(TestBean.Fields.active, TestBean::isActive);
|
.withProp(TestBean.Fields.active, TestBean::isActive);
|
||||||
|
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
|
|
||||||
private String label;
|
private String label;
|
||||||
|
|
||||||
private SubBean content;
|
private SubBeanWithUnquotedValues contentA;
|
||||||
|
|
||||||
|
private SubBeanWithQuotedValues contentB;
|
||||||
|
|
||||||
|
private int value;
|
||||||
private boolean active;
|
private boolean active;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -51,15 +49,41 @@ class StringifyUnitTest {
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Builder
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@FieldNameConstants
|
public static class SubBeanWithUnquotedValues implements Stringifyable {
|
||||||
public static class SubBean implements Stringifyable {
|
|
||||||
|
|
||||||
private static Stringify<SubBean> toString = stringify(SubBean.class)
|
private static Stringify<SubBeanWithUnquotedValues> toString = stringify(SubBeanWithUnquotedValues.class)
|
||||||
.withProp(SubBean.Fields.key, SubBean::getKey)
|
.withProp(SubBeanWithUnquotedValues::getKey)
|
||||||
.withProp(Fields.value, SubBean::getValue);
|
.withProp(SubBeanWithUnquotedValues::getValue)
|
||||||
|
.withSeparator(": ")
|
||||||
|
.quotedValues(false);
|
||||||
|
|
||||||
|
private String key;
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toString.apply(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toShortString() {
|
||||||
|
return key + ":" + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class SubBeanWithQuotedValues implements Stringifyable {
|
||||||
|
|
||||||
|
private static Stringify<SubBeanWithQuotedValues> toString = stringify(SubBeanWithQuotedValues.class)
|
||||||
|
.withProp(SubBeanWithQuotedValues::getKey)
|
||||||
|
.withProp(SubBeanWithQuotedValues::getValue)
|
||||||
|
.withSeparator(": ")
|
||||||
|
.quotedValues(true);
|
||||||
|
|
||||||
private String key;
|
private String key;
|
||||||
private Integer value;
|
private Integer value;
|
||||||
@ -78,22 +102,33 @@ class StringifyUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
void stringifyWhenAllPropsHaveValues() {
|
void stringifyWhenAllPropsHaveValues() {
|
||||||
final var given = new TestBean(UUID.randomUUID(), "some label",
|
final var given = new TestBean(UUID.randomUUID(), "some label",
|
||||||
new SubBean("some content", 1234), false);
|
new SubBeanWithUnquotedValues("some key", "some value"),
|
||||||
|
new SubBeanWithQuotedValues("some key", 1234),
|
||||||
|
42,
|
||||||
|
false);
|
||||||
final var result = given.toString();
|
final var result = given.toString();
|
||||||
assertThat(result).isEqualTo("bean(label='some label', content='some content:1234', active=false)");
|
assertThat(result).isEqualTo(
|
||||||
|
"bean(label='some label', contentA='some key:some value', contentB='some key:1234', value=42, active=false)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void stringifyWhenAllNullablePropsHaveNulValues() {
|
void stringifyWhenAllNullablePropsHaveNulValues() {
|
||||||
final var given = new TestBean();
|
final var given = new TestBean();
|
||||||
final var result = given.toString();
|
final var result = given.toString();
|
||||||
assertThat(result).isEqualTo("bean(active=false)");
|
assertThat(result).isEqualTo("bean(value=0, active=false)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void stringifyWithoutExplicitNameUsesSimpleClassName() {
|
void stringifyWithoutExplicitNameUsesSimpleClassName() {
|
||||||
final var given = new SubBean("some key", 1234);
|
final var given = new SubBeanWithUnquotedValues("some key", "some value");
|
||||||
final var result = given.toString();
|
final var result = given.toString();
|
||||||
assertThat(result).isEqualTo("SubBean(key='some key', value=1234)");
|
assertThat(result).isEqualTo("SubBeanWithUnquotedValues(some key: some value)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void stringifyWithQuotedValueTrueQuotesEvenIntegers() {
|
||||||
|
final var given = new SubBeanWithQuotedValues("some key", 1234);
|
||||||
|
final var result = given.toString();
|
||||||
|
assertThat(result).isEqualTo("SubBeanWithQuotedValues('some key': '1234')");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.office.partner;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class HsOfficePartnerEntityTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toStringContainsPersonAndContact() {
|
||||||
|
final var given = HsOfficePartnerEntity.builder()
|
||||||
|
.person(HsOfficePersonEntity.builder().tradeName("some trade name").build())
|
||||||
|
.contact(HsOfficeContactEntity.builder().label("some label").build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final var result = given.toString();
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo("partner(some trade name: some label)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toShortStringContainsPersonAndContact() {
|
||||||
|
final var given = HsOfficePartnerEntity.builder()
|
||||||
|
.person(HsOfficePersonEntity.builder().tradeName("some trade name").build())
|
||||||
|
.contact(HsOfficeContactEntity.builder().label("some label").build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final var result = given.toShortString();
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo("some trade name");
|
||||||
|
}
|
||||||
|
}
|
@ -150,9 +150,9 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
// then
|
// then
|
||||||
allThesePartnersAreReturned(
|
allThesePartnersAreReturned(
|
||||||
result,
|
result,
|
||||||
"partner(Third OHG, third contact)",
|
"partner(Third OHG: third contact)",
|
||||||
"partner(Second e.K., second contact)",
|
"partner(Second e.K.: second contact)",
|
||||||
"partner(First GmbH, first contact)");
|
"partner(First GmbH: first contact)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -164,7 +164,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
final var result = partnerRepo.findPartnerByOptionalNameLike(null);
|
final var result = partnerRepo.findPartnerByOptionalNameLike(null);
|
||||||
|
|
||||||
// then:
|
// then:
|
||||||
exactlyThesePartnersAreReturned(result, "partner(First GmbH, first contact)");
|
exactlyThesePartnersAreReturned(result, "partner(First GmbH: first contact)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
final var result = partnerRepo.findPartnerByOptionalNameLike("third contact");
|
final var result = partnerRepo.findPartnerByOptionalNameLike("third contact");
|
||||||
|
|
||||||
// then
|
// then
|
||||||
exactlyThesePartnersAreReturned(result, "partner(Third OHG, third contact)");
|
exactlyThesePartnersAreReturned(result, "partner(Third OHG: third contact)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,20 +392,20 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
void cleanup() {
|
void cleanup() {
|
||||||
context("superuser-alex@hostsharing.net", null);
|
context("superuser-alex@hostsharing.net", null);
|
||||||
tempPartners.forEach(tempPartner -> {
|
tempPartners.forEach(tempPartner -> {
|
||||||
System.out.println("DELETING temporary partner: " + tempPartner.getDisplayName());
|
System.out.println("DELETING temporary partner: " + tempPartner.toString());
|
||||||
partnerRepo.deleteByUuid(tempPartner.getUuid());
|
partnerRepo.deleteByUuid(tempPartner.getUuid());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void exactlyThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) {
|
void exactlyThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) {
|
||||||
assertThat(actualResult)
|
assertThat(actualResult)
|
||||||
.extracting(HsOfficePartnerEntity::getDisplayName)
|
.extracting(partnerEntity -> partnerEntity.toString())
|
||||||
.containsExactlyInAnyOrder(partnerNames);
|
.containsExactlyInAnyOrder(partnerNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
void allThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) {
|
void allThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) {
|
||||||
assertThat(actualResult)
|
assertThat(actualResult)
|
||||||
.extracting(HsOfficePartnerEntity::getDisplayName)
|
.extracting(partnerEntity -> partnerEntity.toString())
|
||||||
.contains(partnerNames);
|
.contains(partnerNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user