package name.abuchen.portfolio.datatransfer.actions;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Test;
import name.abuchen.portfolio.datatransfer.ImportAction.Status.Code;
import name.abuchen.portfolio.model.Account;
import name.abuchen.portfolio.model.AccountTransaction;
import name.abuchen.portfolio.model.Portfolio;
import name.abuchen.portfolio.model.PortfolioTransaction;
import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.model.Transaction;
public class DetectDuplicatesActionTest
{
@SuppressWarnings("nls")
@Test
public void testDuplicateDetection4AccountTransaction() throws Exception
{
DetectDuplicatesAction action = new DetectDuplicatesAction();
new PropertyChecker<AccountTransaction>(AccountTransaction.class, "note", "forex", "monetaryAmount")
.before((name, o, c) -> assertThat(name, action.process(o, account(c)).getCode(),
is(Code.WARNING)))
.after((name, o, c) -> assertThat(name, action.process(o, account(c)).getCode(), is(Code.OK)))
.run();
}
@SuppressWarnings("nls")
@Test
public void testDuplicateDetection4PortfolioTransaction() throws Exception
{
DetectDuplicatesAction action = new DetectDuplicatesAction();
new PropertyChecker<PortfolioTransaction>(PortfolioTransaction.class, "fees", "taxes", "note", "forex",
"monetaryAmount") //
.before((name, o, c) -> assertThat(name,
action.process(o, portfolio(c)).getCode(), is(Code.WARNING)))
.after((name, o, c) -> assertThat(name,
action.process(o, portfolio(c)).getCode(), is(Code.OK)))
.run();
}
private Account account(AccountTransaction t)
{
Account a = new Account();
a.addTransaction(t);
return a;
}
private Portfolio portfolio(PortfolioTransaction t)
{
Portfolio p = new Portfolio();
p.addTransaction(t);
return p;
}
@FunctionalInterface
private interface Consumer<T>
{
void accept(String name, T original, T copy);
}
private static class PropertyChecker<T extends Transaction>
{
private Random random = new Random();
private Class<T> type;
private List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>();
private Consumer<T> before;
private Consumer<T> after;
public PropertyChecker(Class<T> type, String... excludes) throws IntrospectionException
{
this.type = type;
Set<String> excludedSet = new HashSet<String>(Arrays.asList(excludes));
BeanInfo info = Introspector.getBeanInfo(type);
for (PropertyDescriptor p : info.getPropertyDescriptors())
{
if (excludedSet.contains(p.getName()))
continue;
if (p.getWriteMethod() == null || p.getReadMethod() == null)
continue;
properties.add(p);
}
}
public PropertyChecker<T> before(Consumer<T> before)
{
this.before = before;
return this;
}
public PropertyChecker<T> after(Consumer<T> after)
{
this.after = after;
return this;
}
public void run() throws Exception
{
for (PropertyDescriptor p : properties)
check(p);
}
private void check(PropertyDescriptor change) throws Exception
{
T instance = type.newInstance();
T other = type.newInstance();
for (PropertyDescriptor p : properties)
{
Object argument = arg(p.getPropertyType());
p.getWriteMethod().invoke(instance, argument);
p.getWriteMethod().invoke(other, argument);
}
before.accept(change.getName(), instance, other);
change.getWriteMethod().invoke(other,
alternative(change.getPropertyType(), change.getReadMethod().invoke(other)));
after.accept(change.getName(), instance, other);
}
private Object alternative(Class<?> propertyType, Object value)
{
if (propertyType == String.class)
{
return "x" + value; //$NON-NLS-1$
}
else if (propertyType == long.class)
{
return ((long) value) + 1;
}
else if (propertyType == LocalDate.class)
{
return LocalDate.of(1999, 1, 1);
}
else if (propertyType == Security.class)
{
return new Security();
}
else if (propertyType == AccountTransaction.Type.class)
{
return AccountTransaction.Type.values()[(((AccountTransaction.Type) value).ordinal() + 1)
% AccountTransaction.Type.values().length];
}
else if (propertyType == PortfolioTransaction.Type.class)
{
return PortfolioTransaction.Type.values()[(((PortfolioTransaction.Type) value).ordinal() + 1)
% PortfolioTransaction.Type.values().length];
}
else
{
throw new UnsupportedOperationException(propertyType.getName());
}
}
private Object arg(Class<?> propertyType)
{
if (propertyType == String.class)
{
return RandomStringUtils.random(10);
}
else if (propertyType == long.class)
{
return random.nextLong();
}
else if (propertyType == LocalDate.class)
{
return LocalDate.of(2000 + random.nextInt(30), 1, 1);
}
else if (propertyType == Security.class)
{
return new Security();
}
else if (propertyType == AccountTransaction.Type.class)
{
return AccountTransaction.Type.values()[random.nextInt(AccountTransaction.Type.values().length)];
}
else if (propertyType == PortfolioTransaction.Type.class)
{
return PortfolioTransaction.Type.values()[random.nextInt(PortfolioTransaction.Type.values().length)];
}
else
{
throw new UnsupportedOperationException(propertyType.getName());
}
}
}
}